summaryrefslogtreecommitdiff
path: root/spec/features
diff options
context:
space:
mode:
Diffstat (limited to 'spec/features')
-rw-r--r--spec/features/action_cable_logging_spec.rb6
-rw-r--r--spec/features/admin/admin_appearance_spec.rb5
-rw-r--r--spec/features/admin/admin_dev_ops_report_spec.rb6
-rw-r--r--spec/features/admin/admin_groups_spec.rb10
-rw-r--r--spec/features/admin/admin_labels_spec.rb2
-rw-r--r--spec/features/admin/admin_mode/logout_spec.rb70
-rw-r--r--spec/features/admin/admin_mode_spec.rb235
-rw-r--r--spec/features/admin/admin_projects_spec.rb36
-rw-r--r--spec/features/admin/admin_settings_spec.rb73
-rw-r--r--spec/features/admin/admin_users_spec.rb44
-rw-r--r--spec/features/admin/services/admin_visits_service_templates_spec.rb2
-rw-r--r--spec/features/admin/users/user_spec.rb177
-rw-r--r--spec/features/admin/users/users_spec.rb586
-rw-r--r--spec/features/boards/boards_spec.rb49
-rw-r--r--spec/features/boards/new_issue_spec.rb93
-rw-r--r--spec/features/boards/sidebar_assignee_spec.rb25
-rw-r--r--spec/features/boards/sidebar_labels_in_namespaces_spec.rb34
-rw-r--r--spec/features/boards/sub_group_project_spec.rb46
-rw-r--r--spec/features/boards/user_visits_board_spec.rb78
-rw-r--r--spec/features/calendar_spec.rb4
-rw-r--r--spec/features/dashboard/active_tab_spec.rb56
-rw-r--r--spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb74
-rw-r--r--spec/features/dashboard/shortcuts_spec.rb106
-rw-r--r--spec/features/dashboard/todos/todos_spec.rb2
-rw-r--r--spec/features/frequently_visited_projects_and_groups_spec.rb76
-rw-r--r--spec/features/groups/group_page_with_external_authorization_service_spec.rb6
-rw-r--r--spec/features/groups/issues_spec.rb2
-rw-r--r--spec/features/groups/members/manage_groups_spec.rb42
-rw-r--r--spec/features/groups/members/manage_members_spec.rb110
-rw-r--r--spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb12
-rw-r--r--spec/features/groups/milestone_spec.rb4
-rw-r--r--spec/features/groups/milestones/gfm_autocomplete_spec.rb80
-rw-r--r--spec/features/groups/navbar_spec.rb62
-rw-r--r--spec/features/groups/settings/packages_and_registries_spec.rb31
-rw-r--r--spec/features/groups_spec.rb41
-rw-r--r--spec/features/invites_spec.rb319
-rw-r--r--spec/features/issuables/sorting_list_spec.rb36
-rw-r--r--spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb22
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb81
-rw-r--r--spec/features/issues/resource_label_events_spec.rb2
-rw-r--r--spec/features/issues/service_desk_spec.rb4
-rw-r--r--spec/features/issues/spam_issues_spec.rb1
-rw-r--r--spec/features/issues/user_bulk_edits_issues_labels_spec.rb (renamed from spec/features/issues/bulk_assignment_labels_spec.rb)91
-rw-r--r--spec/features/issues/user_bulk_edits_issues_spec.rb (renamed from spec/features/issues/update_issues_spec.rb)46
-rw-r--r--spec/features/issues/user_edits_issue_spec.rb8
-rw-r--r--spec/features/issues/user_interacts_with_awards_spec.rb53
-rw-r--r--spec/features/issues/user_toggles_subscription_spec.rb4
-rw-r--r--spec/features/markdown/copy_as_gfm_spec.rb4
-rw-r--r--spec/features/markdown/math_spec.rb18
-rw-r--r--spec/features/merge_request/batch_comments_spec.rb4
-rw-r--r--spec/features/merge_request/user_creates_merge_request_spec.rb1
-rw-r--r--spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb6
-rw-r--r--spec/features/merge_request/user_posts_notes_spec.rb1
-rw-r--r--spec/features/merge_request/user_resolves_conflicts_spec.rb2
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb8
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_pipelines_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_wip_help_message_spec.rb2
-rw-r--r--spec/features/merge_request/user_views_open_merge_request_spec.rb2
-rw-r--r--spec/features/merge_requests/user_mass_updates_spec.rb32
-rw-r--r--spec/features/monitor_sidebar_link_spec.rb145
-rw-r--r--spec/features/operations_sidebar_link_spec.rb144
-rw-r--r--spec/features/populate_new_pipeline_vars_with_params_spec.rb17
-rw-r--r--spec/features/profiles/chat_names_spec.rb6
-rw-r--r--spec/features/profiles/user_edit_preferences_spec.rb19
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb24
-rw-r--r--spec/features/profiles/user_visits_notifications_tab_spec.rb8
-rw-r--r--spec/features/project_variables_spec.rb2
-rw-r--r--spec/features/projects/active_tabs_spec.rb58
-rw-r--r--spec/features/projects/badges/pipeline_badge_spec.rb1
-rw-r--r--spec/features/projects/blobs/blob_show_spec.rb75
-rw-r--r--spec/features/projects/branches/user_deletes_branch_spec.rb1
-rw-r--r--spec/features/projects/commit/builds_spec.rb2
-rw-r--r--spec/features/projects/commit/cherry_pick_spec.rb1
-rw-r--r--spec/features/projects/commit/user_comments_on_commit_spec.rb1
-rw-r--r--spec/features/projects/commit/user_reverts_commit_spec.rb1
-rw-r--r--spec/features/projects/commit/user_views_user_status_on_commit_spec.rb1
-rw-r--r--spec/features/projects/compare_spec.rb28
-rw-r--r--spec/features/projects/confluence/user_views_confluence_page_spec.rb1
-rw-r--r--spec/features/projects/deploy_keys_spec.rb5
-rw-r--r--spec/features/projects/diffs/diff_show_spec.rb2
-rw-r--r--spec/features/projects/features_visibility_spec.rb17
-rw-r--r--spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb69
-rw-r--r--spec/features/projects/files/user_edits_files_spec.rb4
-rw-r--r--spec/features/projects/fork_spec.rb4
-rw-r--r--spec/features/projects/graph_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_asana_spec.rb (renamed from spec/features/projects/services/user_activates_asana_spec.rb)0
-rw-r--r--spec/features/projects/integrations/user_activates_assembla_spec.rb (renamed from spec/features/projects/services/user_activates_assembla_spec.rb)0
-rw-r--r--spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb (renamed from spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb)0
-rw-r--r--spec/features/projects/issues/design_management/user_views_design_images_spec.rb1
-rw-r--r--spec/features/projects/jobs_spec.rb4
-rw-r--r--spec/features/projects/labels/issues_sorted_by_priority_spec.rb24
-rw-r--r--spec/features/projects/labels/user_sees_links_to_issuables_spec.rb1
-rw-r--r--spec/features/projects/labels/user_views_labels_spec.rb1
-rw-r--r--spec/features/projects/members/invite_group_spec.rb106
-rw-r--r--spec/features/projects/members/list_spec.rb27
-rw-r--r--spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb14
-rw-r--r--spec/features/projects/merge_request_button_spec.rb1
-rw-r--r--spec/features/projects/milestones/gfm_autocomplete_spec.rb80
-rw-r--r--spec/features/projects/navbar_spec.rb166
-rw-r--r--spec/features/projects/new_project_from_template_spec.rb26
-rw-r--r--spec/features/projects/new_project_spec.rb560
-rw-r--r--spec/features/projects/pages/user_adds_domain_spec.rb1
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb1
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb70
-rw-r--r--spec/features/projects/product_analytics/events_spec.rb1
-rw-r--r--spec/features/projects/releases/user_views_releases_spec.rb172
-rw-r--r--spec/features/projects/services/user_activates_issue_tracker_spec.rb2
-rw-r--r--spec/features/projects/settings/access_tokens_spec.rb2
-rw-r--r--spec/features/projects/settings/monitor_settings_spec.rb (renamed from spec/features/projects/settings/operations_settings_spec.rb)26
-rw-r--r--spec/features/projects/settings/packages_settings_spec.rb26
-rw-r--r--spec/features/projects/settings/project_settings_spec.rb1
-rw-r--r--spec/features/projects/settings/registry_settings_spec.rb160
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb4
-rw-r--r--spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb12
-rw-r--r--spec/features/projects/settings/user_manages_project_members_spec.rb28
-rw-r--r--spec/features/projects/snippets/user_views_snippets_spec.rb1
-rw-r--r--spec/features/projects/user_changes_project_visibility_spec.rb4
-rw-r--r--spec/features/projects/user_sees_sidebar_spec.rb2
-rw-r--r--spec/features/projects/user_sees_user_popover_spec.rb1
-rw-r--r--spec/features/projects/user_uses_shortcuts_spec.rb67
-rw-r--r--spec/features/runners_spec.rb96
-rw-r--r--spec/features/snippets/spam_snippets_spec.rb1
-rw-r--r--spec/features/unsubscribe_links_spec.rb2
-rw-r--r--spec/features/user_can_display_performance_bar_spec.rb36
-rw-r--r--spec/features/users/add_email_to_existing_account_spec.rb18
-rw-r--r--spec/features/users/signup_spec.rb12
-rw-r--r--spec/features/users/user_browses_projects_on_user_page_spec.rb2
-rw-r--r--spec/features/whats_new_spec.rb18
134 files changed, 3270 insertions, 2191 deletions
diff --git a/spec/features/action_cable_logging_spec.rb b/spec/features/action_cable_logging_spec.rb
index ce7c0e03aad..2e6ce93f7f7 100644
--- a/spec/features/action_cable_logging_spec.rb
+++ b/spec/features/action_cable_logging_spec.rb
@@ -22,11 +22,7 @@ RSpec.describe 'ActionCable logging', :js do
subscription_data = a_hash_including(
remote_ip: '127.0.0.1',
user_id: user.id,
- username: user.username,
- params: a_hash_including(
- project_path: project.full_path,
- iid: issue.iid.to_s
- )
+ username: user.username
)
expect(ActiveSupport::Notifications).to receive(:instrument).with('subscribe.action_cable', subscription_data)
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb
index 61e7efbc56c..603e757096f 100644
--- a/spec/features/admin/admin_appearance_spec.rb
+++ b/spec/features/admin/admin_appearance_spec.rb
@@ -37,7 +37,7 @@ RSpec.describe 'Admin Appearance' do
expect_custom_sign_in_appearance(appearance)
end
- it 'preview new project page appearance' do
+ it 'preview new project page appearance', :js do
sign_in(admin)
gitlab_enable_admin_mode_sign_in(admin)
@@ -86,10 +86,11 @@ RSpec.describe 'Admin Appearance' do
expect_custom_sign_in_appearance(appearance)
end
- it 'custom new project page' do
+ it 'custom new project page', :js do
sign_in(admin)
gitlab_enable_admin_mode_sign_in(admin)
visit new_project_path
+ find('[data-qa-selector="blank_project_link"]').click
expect_custom_new_project_appearance(appearance)
end
diff --git a/spec/features/admin/admin_dev_ops_report_spec.rb b/spec/features/admin/admin_dev_ops_report_spec.rb
index a05fa0640d8..33f984af807 100644
--- a/spec/features/admin/admin_dev_ops_report_spec.rb
+++ b/spec/features/admin/admin_dev_ops_report_spec.rb
@@ -53,15 +53,13 @@ RSpec.describe 'DevOps Report page', :js do
end
context 'when there is data to display' do
- it 'shows numbers for each metric' do
+ it 'shows the DevOps Score app' do
stub_application_setting(usage_ping_enabled: true)
create(:dev_ops_report_metric)
visit admin_dev_ops_report_path
- expect(page).to have_content(
- 'Issues created per active user 1.2 You 9.3 Lead 13.3%'
- )
+ expect(page).to have_selector('[data-testid="devops-score-app"]')
end
end
end
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index e7634f4e020..f9673a8aa2f 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe 'Admin Groups' do
include Select2Helper
include Spec::Support::Helpers::Features::MembersHelpers
+ include Spec::Support::Helpers::Features::InviteMembersModalHelper
let(:internal) { Gitlab::VisibilityLevel::INTERNAL }
@@ -202,6 +203,7 @@ RSpec.describe 'Admin Groups' do
select2(Gitlab::Access::REPORTER, from: '#access_level')
end
click_button "Add users to group"
+
page.within ".group-users-list" do
expect(page).to have_content(user.name)
expect(page).to have_content('Reporter')
@@ -220,19 +222,13 @@ RSpec.describe 'Admin Groups' do
describe 'add admin himself to a group' do
before do
- stub_feature_flags(invite_members_group_modal: false)
group.add_user(:user, Gitlab::Access::OWNER)
end
it 'adds admin a to a group as developer', :js do
visit group_group_members_path(group)
- page.within '.invite-users-form' do
- select2(current_user.id, from: '#user_ids', multiple: true)
- select 'Developer', from: 'access_level'
- end
-
- click_button 'Invite'
+ invite_member(current_user.name, role: 'Developer')
page.within members_table do
expect(page).to have_content(current_user.name)
diff --git a/spec/features/admin/admin_labels_spec.rb b/spec/features/admin/admin_labels_spec.rb
index 43fb1f31a0f..08d81906d9f 100644
--- a/spec/features/admin/admin_labels_spec.rb
+++ b/spec/features/admin/admin_labels_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe 'admin issues labels' do
it 'deletes all labels', :js do
page.within '.labels' do
- page.all('.js-remove-row').each do |remove|
+ page.all('.js-remove-label').each do |remove|
accept_confirm { remove.click }
wait_for_requests
end
diff --git a/spec/features/admin/admin_mode/logout_spec.rb b/spec/features/admin/admin_mode/logout_spec.rb
index 8cfac5d8b99..664eb51e58f 100644
--- a/spec/features/admin/admin_mode/logout_spec.rb
+++ b/spec/features/admin/admin_mode/logout_spec.rb
@@ -8,37 +8,67 @@ RSpec.describe 'Admin Mode Logout', :js do
let(:user) { create(:admin) }
- before do
- stub_feature_flags(combined_menu: false)
+ shared_examples 'combined_menu: feature flag examples' do
+ before do
+ gitlab_sign_in(user)
+ gitlab_enable_admin_mode_sign_in(user)
+ visit admin_root_path
+ end
- gitlab_sign_in(user)
- gitlab_enable_admin_mode_sign_in(user)
- visit admin_root_path
- end
+ it 'disable removes admin mode and redirects to root page' do
+ pending_on_combined_menu_flag
- it 'disable removes admin mode and redirects to root page' do
- gitlab_disable_admin_mode
+ gitlab_disable_admin_mode
- expect(current_path).to eq root_path
- expect(page).to have_link(href: new_admin_session_path)
- end
+ expect(current_path).to eq root_path
+ expect(page).to have_link(href: new_admin_session_path)
+ end
+
+ it 'disable shows flash notice' do
+ pending_on_combined_menu_flag
+
+ gitlab_disable_admin_mode
+
+ expect(page).to have_selector('.flash-notice')
+ end
- it 'disable shows flash notice' do
- gitlab_disable_admin_mode
+ context 'on a read-only instance' do
+ before do
+ allow(Gitlab::Database).to receive(:read_only?).and_return(true)
+ end
- expect(page).to have_selector('.flash-notice')
+ it 'disable removes admin mode and redirects to root page' do
+ pending_on_combined_menu_flag
+
+ gitlab_disable_admin_mode
+
+ expect(current_path).to eq root_path
+ expect(page).to have_link(href: new_admin_session_path)
+ end
+ end
end
- context 'on a read-only instance' do
+ context 'with combined_menu: feature flag on' do
+ let(:needs_rewrite_for_combined_menu_flag_on) { true }
+
before do
- allow(Gitlab::Database).to receive(:read_only?).and_return(true)
+ stub_feature_flags(combined_menu: true)
end
- it 'disable removes admin mode and redirects to root page' do
- gitlab_disable_admin_mode
+ it_behaves_like 'combined_menu: feature flag examples'
+ end
- expect(current_path).to eq root_path
- expect(page).to have_link(href: new_admin_session_path)
+ context 'with combined_menu feature flag off' do
+ let(:needs_rewrite_for_combined_menu_flag_on) { false }
+
+ before do
+ stub_feature_flags(combined_menu: false)
end
+
+ it_behaves_like 'combined_menu: feature flag examples'
+ end
+
+ def pending_on_combined_menu_flag
+ pending 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587' if needs_rewrite_for_combined_menu_flag_on
end
end
diff --git a/spec/features/admin/admin_mode_spec.rb b/spec/features/admin/admin_mode_spec.rb
index 633de20c82d..4df035b13e8 100644
--- a/spec/features/admin/admin_mode_spec.rb
+++ b/spec/features/admin/admin_mode_spec.rb
@@ -8,55 +8,41 @@ RSpec.describe 'Admin mode' do
let(:admin) { create(:admin) }
- before do
- stub_feature_flags(combined_menu: false)
-
- stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
- end
-
- context 'application setting :admin_mode is enabled', :request_store do
+ shared_examples 'combined_menu: feature flag examples' do
before do
- sign_in(admin)
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
end
- context 'when not in admin mode' do
- it 'has no leave admin mode button' do
- visit new_admin_session_path
-
- page.within('.navbar-sub-nav') do
- expect(page).not_to have_link(href: destroy_admin_session_path)
- end
+ context 'application setting :admin_mode is enabled', :request_store do
+ before do
+ sign_in(admin)
end
- it 'can open pages not in admin scope' do
- visit new_admin_session_path
+ context 'when not in admin mode' do
+ it 'has no leave admin mode button' do
+ visit new_admin_session_path
- page.within('.navbar-sub-nav') do
- find_all('a', text: 'Projects').first.click
+ page.within('.navbar-sub-nav') do
+ expect(page).not_to have_link(href: destroy_admin_session_path)
+ end
end
- expect(page).to have_current_path(dashboard_projects_path)
- end
-
- it 'is necessary to provide credentials again before opening pages in admin scope' do
- visit general_admin_application_settings_path # admin logged out because not in admin_mode
-
- expect(page).to have_current_path(new_admin_session_path)
- end
+ it 'can open pages not in admin scope' do
+ pending_on_combined_menu_flag
- it 'can enter admin mode' do
- visit new_admin_session_path
+ visit new_admin_session_path
- fill_in 'user_password', with: admin.password
+ page.within('.navbar-sub-nav') do
+ find_all('a', text: 'Projects').first.click
+ end
- click_button 'Enter Admin Mode'
+ expect(page).to have_current_path(dashboard_projects_path)
+ end
- expect(page).to have_current_path(admin_root_path)
- end
+ it 'is necessary to provide credentials again before opening pages in admin scope' do
+ visit general_admin_application_settings_path # admin logged out because not in admin_mode
- context 'on a read-only instance' do
- before do
- allow(Gitlab::Database).to receive(:read_only?).and_return(true)
+ expect(page).to have_current_path(new_admin_session_path)
end
it 'can enter admin mode' do
@@ -68,108 +54,161 @@ RSpec.describe 'Admin mode' do
expect(page).to have_current_path(admin_root_path)
end
- end
- end
- context 'when in admin_mode' do
- before do
- gitlab_enable_admin_mode_sign_in(admin)
- end
+ context 'on a read-only instance' do
+ before do
+ allow(Gitlab::Database).to receive(:read_only?).and_return(true)
+ end
- it 'contains link to leave admin mode' do
- page.within('.navbar-sub-nav') do
- expect(page).to have_link(href: destroy_admin_session_path)
+ it 'can enter admin mode' do
+ visit new_admin_session_path
+
+ fill_in 'user_password', with: admin.password
+
+ click_button 'Enter Admin Mode'
+
+ expect(page).to have_current_path(admin_root_path)
+ end
end
end
- it 'can leave admin mode using main dashboard link', :js do
- page.within('.navbar-sub-nav') do
- click_on 'Leave Admin Mode'
+ context 'when in admin_mode' do
+ before do
+ gitlab_enable_admin_mode_sign_in(admin)
+ end
- expect(page).to have_link(href: new_admin_session_path)
+ it 'contains link to leave admin mode' do
+ pending_on_combined_menu_flag
+
+ page.within('.navbar-sub-nav') do
+ expect(page).to have_link(href: destroy_admin_session_path)
+ end
end
- end
- it 'can leave admin mode using dropdown menu on smaller screens', :js do
- resize_screen_xs
- visit root_dashboard_path
+ it 'can leave admin mode using main dashboard link', :js do
+ pending_on_combined_menu_flag
- find('.header-more').click
+ page.within('.navbar-sub-nav') do
+ click_on 'Leave Admin Mode'
- page.within '.navbar-sub-nav' do
- click_on 'Leave Admin Mode'
+ expect(page).to have_link(href: new_admin_session_path)
+ end
+ end
+
+ it 'can leave admin mode using dropdown menu on smaller screens', :js do
+ pending_on_combined_menu_flag
+
+ resize_screen_xs
+ visit root_dashboard_path
find('.header-more').click
- expect(page).to have_link(href: new_admin_session_path)
- end
- end
+ page.within '.navbar-sub-nav' do
+ click_on 'Leave Admin Mode'
- it 'can open pages not in admin scope' do
- page.within('.navbar-sub-nav') do
- find_all('a', text: 'Projects').first.click
+ find('.header-more').click
- expect(page).to have_current_path(dashboard_projects_path)
+ expect(page).to have_link(href: new_admin_session_path)
+ end
end
- end
- context 'nav bar' do
- it 'shows admin dashboard links on bigger screen' do
- visit root_dashboard_path
+ it 'can open pages not in admin scope' do
+ pending_on_combined_menu_flag
- page.within '.navbar' do
- expect(page).to have_link(text: 'Admin Area', href: admin_root_path, visible: true)
- expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
+ page.within('.navbar-sub-nav') do
+ find_all('a', text: 'Projects').first.click
+
+ expect(page).to have_current_path(dashboard_projects_path)
end
end
- it 'relocates admin dashboard links to dropdown list on smaller screen', :js do
- resize_screen_xs
- visit root_dashboard_path
+ context 'nav bar' do
+ it 'shows admin dashboard links on bigger screen' do
+ pending_on_combined_menu_flag
- page.within '.navbar' do
- expect(page).not_to have_link(text: 'Admin Area', href: admin_root_path, visible: true)
- expect(page).not_to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
+ visit root_dashboard_path
+
+ page.within '.navbar' do
+ expect(page).to have_link(text: 'Admin Area', href: admin_root_path, visible: true)
+ expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
+ end
end
- find('.header-more').click
+ it 'relocates admin dashboard links to dropdown list on smaller screen', :js do
+ pending_on_combined_menu_flag
+
+ resize_screen_xs
+ visit root_dashboard_path
- page.within '.navbar' do
- expect(page).to have_link(text: 'Admin Area', href: admin_root_path, visible: true)
- expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
+ page.within '.navbar' do
+ expect(page).not_to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
+ end
+
+ find('.header-more').click
+
+ page.within '.navbar' do
+ expect(page).to have_link(text: 'Admin Area', href: admin_root_path, visible: true)
+ expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
+ end
end
end
- end
- context 'on a read-only instance' do
- before do
- allow(Gitlab::Database).to receive(:read_only?).and_return(true)
- end
+ context 'on a read-only instance' do
+ before do
+ allow(Gitlab::Database).to receive(:read_only?).and_return(true)
+ end
- it 'can leave admin mode', :js do
- page.within('.navbar-sub-nav') do
- click_on 'Leave Admin Mode'
+ it 'can leave admin mode', :js do
+ pending_on_combined_menu_flag
- expect(page).to have_link(href: new_admin_session_path)
+ page.within('.navbar-sub-nav') do
+ click_on 'Leave Admin Mode'
+
+ expect(page).to have_link(href: new_admin_session_path)
+ end
end
end
end
end
+
+ context 'application setting :admin_mode is disabled' do
+ before do
+ stub_application_setting(admin_mode: false)
+ sign_in(admin)
+ end
+
+ it 'shows no admin mode buttons in navbar' do
+ visit admin_root_path
+
+ page.within('.navbar-sub-nav') do
+ expect(page).not_to have_link(href: new_admin_session_path)
+ expect(page).not_to have_link(href: destroy_admin_session_path)
+ end
+ end
+ end
end
- context 'application setting :admin_mode is disabled' do
+ context 'with combined_menu: feature flag on' do
+ let(:needs_rewrite_for_combined_menu_flag_on) { true }
+
before do
- stub_application_setting(admin_mode: false)
- sign_in(admin)
+ stub_feature_flags(combined_menu: true)
end
- it 'shows no admin mode buttons in navbar' do
- visit admin_root_path
+ it_behaves_like 'combined_menu: feature flag examples'
+ end
- page.within('.navbar-sub-nav') do
- expect(page).not_to have_link(href: new_admin_session_path)
- expect(page).not_to have_link(href: destroy_admin_session_path)
- end
+ context 'with combined_menu feature flag off' do
+ let(:needs_rewrite_for_combined_menu_flag_on) { false }
+
+ before do
+ stub_feature_flags(combined_menu: false)
end
+
+ it_behaves_like 'combined_menu: feature flag examples'
+ end
+
+ def pending_on_combined_menu_flag
+ pending 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587' if needs_rewrite_for_combined_menu_flag_on
end
end
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb
index bf280595ec7..cbbe9aa3b8b 100644
--- a/spec/features/admin/admin_projects_spec.rb
+++ b/spec/features/admin/admin_projects_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe "Admin::Projects" do
include Spec::Support::Helpers::Features::MembersHelpers
+ include Spec::Support::Helpers::Features::InviteMembersModalHelper
include Select2Helper
let(:user) { create :user }
@@ -95,21 +96,27 @@ RSpec.describe "Admin::Projects" do
describe 'admin adds themselves to the project', :js do
before do
project.add_maintainer(user)
- stub_feature_flags(invite_members_group_modal: false)
end
it 'adds admin to the project as developer' 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'
+ invite_member(current_user.name, role: 'Developer')
expect(find_member_row(current_user)).to have_content('Developer')
end
+
+ context 'with the invite_members_group_modal feature flag disabled' do
+ it 'adds admin to the project as developer' do
+ stub_feature_flags(invite_members_group_modal: false)
+
+ visit project_project_members_path(project)
+
+ add_member_using_form(current_user.id, role: 'Developer')
+
+ expect(find_member_row(current_user)).to have_content('Developer')
+ end
+ end
end
describe 'admin removes themselves from the project', :js do
@@ -134,4 +141,19 @@ RSpec.describe "Admin::Projects" do
expect(current_path).to match dashboard_projects_path
end
end
+
+ # temporary method for the form until the :invite_members_group_modal feature flag is
+ # enabled: https://gitlab.com/gitlab-org/gitlab/-/issues/247208
+ def add_member_using_form(id, role: 'Developer')
+ page.within '.invite-users-form' do
+ select2(id, from: '#user_ids', multiple: true)
+
+ fill_in 'expires_at', with: 5.days.from_now.to_date
+ find_field('expires_at').native.send_keys :enter
+
+ select(role, from: "access_level")
+
+ click_on 'Invite'
+ end
+ end
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 90ce865cc00..0a7113a5559 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -251,40 +251,62 @@ RSpec.describe 'Admin updates settings' do
end
end
- context 'when the Slack Notifications Service template is active' do
+ context 'when Service Templates are enabled' do
before do
- create(:service, :template, type: 'SlackService', active: true)
-
+ stub_feature_flags(disable_service_templates: false)
visit general_admin_application_settings_path
end
- it 'change Slack Notifications Service template settings', :js do
- first(:link, 'Service Templates').click
- click_link 'Slack notifications'
- fill_in 'Webhook', with: 'http://localhost'
- fill_in 'Username', with: 'test_user'
- fill_in 'service[push_channel]', with: '#test_channel'
- page.check('Notify only broken pipelines')
- page.select 'All branches', from: 'Branches to be notified'
+ it 'shows Service Templates link' do
+ expect(page).to have_link('Service Templates')
+ end
- check_all_events
- click_button 'Save changes'
+ context 'when the Slack Notifications Service template is active' do
+ before do
+ create(:service, :template, type: 'SlackService', active: true)
- expect(page).to have_content 'Application settings saved successfully'
+ visit general_admin_application_settings_path
+ end
- click_link 'Slack notifications'
+ it 'change Slack Notifications Service template settings', :js do
+ first(:link, 'Service Templates').click
+ click_link 'Slack notifications'
+ fill_in 'Webhook', with: 'http://localhost'
+ fill_in 'Username', with: 'test_user'
+ fill_in 'service[push_channel]', with: '#test_channel'
+ page.check('Notify only broken pipelines')
+ page.select 'All branches', from: 'Branches to be notified'
+ page.select 'Match any of the labels', from: 'Labels to be notified behavior'
+
+ check_all_events
+ click_button 'Save changes'
+
+ expect(page).to have_content 'Application settings saved successfully'
- expect(page.all('input[type=checkbox]')).to all(be_checked)
- expect(find_field('Webhook').value).to eq 'http://localhost'
- expect(find_field('Username').value).to eq 'test_user'
- expect(find('[name="service[push_channel]"]').value).to eq '#test_channel'
+ click_link 'Slack notifications'
+
+ expect(page.all('input[type=checkbox]')).to all(be_checked)
+ expect(find_field('Webhook').value).to eq 'http://localhost'
+ expect(find_field('Username').value).to eq 'test_user'
+ expect(find('[name="service[push_channel]"]').value).to eq '#test_channel'
+ end
+
+ it 'defaults Deployment events to false for chat notification template settings', :js do
+ first(:link, 'Service Templates').click
+ click_link 'Slack notifications'
+
+ expect(find_field('Deployment')).not_to be_checked
+ end
end
+ end
- it 'defaults Deployment events to false for chat notification template settings', :js do
- first(:link, 'Service Templates').click
- click_link 'Slack notifications'
+ context 'When Service templates are disabled' do
+ before do
+ stub_feature_flags(disable_service_templates: true)
+ end
- expect(find_field('Deployment')).not_to be_checked
+ it 'does not show Service Templates link' do
+ expect(page).not_to have_link('Service Templates')
end
end
@@ -424,7 +446,8 @@ RSpec.describe 'Admin updates settings' do
check 'Enable reCAPTCHA for login'
fill_in 'IPs per user', with: 15
check 'Enable Spam Check via external API endpoint'
- fill_in 'URL of the external Spam Check endpoint', with: 'https://www.example.com/spamcheck'
+ fill_in 'URL of the external Spam Check endpoint', with: 'grpc://www.example.com/spamcheck'
+ fill_in 'Spam Check API Key', with: 'SPAM_CHECK_API_KEY'
click_button 'Save changes'
end
@@ -433,7 +456,7 @@ RSpec.describe 'Admin updates settings' do
expect(current_settings.login_recaptcha_protection_enabled).to be true
expect(current_settings.unique_ips_limit_per_user).to eq(15)
expect(current_settings.spam_check_endpoint_enabled).to be true
- expect(current_settings.spam_check_endpoint_url).to eq 'https://www.example.com/spamcheck'
+ expect(current_settings.spam_check_endpoint_url).to eq 'grpc://www.example.com/spamcheck'
end
end
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index 4fc60d17886..6d5944002a1 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -10,61 +10,51 @@ RSpec.describe "Admin::Users" do
gitlab_enable_admin_mode_sign_in(current_user)
end
- describe 'Tabs', :js do
+ describe 'Tabs' 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
+ it 'links to the Users tab' do
+ visit cohorts_admin_users_path
within tabs_selector do
click_link 'Users'
+
+ expect(page).to have_selector active_tab_selector, text: '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
+ it 'links to the Cohorts tab' do
visit admin_users_path
within tabs_selector do
click_link 'Cohorts'
+
+ expect(page).to have_selector active_tab_selector, text: 'Cohorts'
end
- expect(page).to have_current_path(admin_users_path(tab: 'cohorts'))
+ expect(page).to have_current_path(cohorts_admin_users_path)
+ expect(page).to have_selector active_tab_selector, text: 'Cohorts'
end
- it 'shows the cohorts tab when the tab param is set' do
+ it 'redirects legacy route' do
visit admin_users_path(tab: 'cohorts')
- within tabs_selector do
- expect(page).to have_selector active_tab_selector, text: 'Cohorts'
- end
+ expect(page).to have_current_path(cohorts_admin_users_path)
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)
+ it 'shows users count per month' do
+ stub_application_setting(usage_ping_enabled: false)
- create_list(:user, 2)
+ 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')
+ visit admin_users_path(tab: 'cohorts')
- expect(page).to have_selector(".js-empty-state")
- expect(page).to have_content("Activate user activity analysis")
- end
+ expect(page).to have_content("#{Time.now.strftime('%b %Y')} 3 0")
end
end
end
diff --git a/spec/features/admin/services/admin_visits_service_templates_spec.rb b/spec/features/admin/services/admin_visits_service_templates_spec.rb
index 1fd8c8316e3..9d011b97f63 100644
--- a/spec/features/admin/services/admin_visits_service_templates_spec.rb
+++ b/spec/features/admin/services/admin_visits_service_templates_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe 'Admin visits service templates' do
let(:admin) { create(:user, :admin) }
- let(:slack_service) { Service.for_template.find { |s| s.type == 'SlackService' } }
+ let(:slack_service) { Integration.for_template.find { |s| s.type == 'SlackService' } }
before do
sign_in(admin)
diff --git a/spec/features/admin/users/user_spec.rb b/spec/features/admin/users/user_spec.rb
index befa7bd338b..01341398135 100644
--- a/spec/features/admin/users/user_spec.rb
+++ b/spec/features/admin/users/user_spec.rb
@@ -4,18 +4,16 @@ require 'spec_helper'
RSpec.describe 'Admin::Users::User' do
let_it_be(:user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
- let_it_be(:current_user) { create(:admin, last_activity_on: 5.days.ago) }
+ let_it_be(:current_user) { create(:admin) }
before do
sign_in(current_user)
gitlab_enable_admin_mode_sign_in(current_user)
- stub_feature_flags(vue_admin_users: false)
end
describe 'GET /admin/users/:id' do
it 'has user info', :aggregate_failures do
- visit admin_users_path
- click_link user.name
+ visit admin_user_path(user)
expect(page).to have_content(user.email)
expect(page).to have_content(user.name)
@@ -27,21 +25,6 @@ RSpec.describe 'Admin::Users::User' do
expect(page).to have_button('Delete user and contributions')
end
- context 'user pending approval' do
- it 'shows user info', :aggregate_failures do
- user = create(:user, :blocked_pending_approval)
-
- visit admin_users_path
- click_link 'Pending approval'
- click_link user.name
-
- expect(page).to have_content(user.name)
- expect(page).to have_content('Pending approval')
- expect(page).to have_link('Approve user')
- expect(page).to have_link('Reject request')
- end
- end
-
context 'when blocking/unblocking the user' do
it 'shows confirmation and allows blocking and unblocking', :js do
visit admin_user_path(user)
@@ -171,6 +154,8 @@ RSpec.describe 'Admin::Users::User' do
it 'logs in as the user when impersonate is clicked' do
subject
+ find('[data-qa-selector="user_menu"]').click
+
expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eql(another_user.username)
end
@@ -205,6 +190,8 @@ RSpec.describe 'Admin::Users::User' do
it 'logs out of impersonated user back to original user' do
subject
+ find('[data-qa-selector="user_menu"]').click
+
expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eq(current_user.username)
end
@@ -238,6 +225,8 @@ RSpec.describe 'Admin::Users::User' do
end
it 'shows when disabled' do
+ user.update!(otp_required_for_login: false)
+
visit admin_user_path(user)
expect_two_factor_status('Disabled')
@@ -251,7 +240,7 @@ RSpec.describe 'Admin::Users::User' do
end
describe 'Email verification status' do
- let!(:secondary_email) do
+ let_it_be(:secondary_email) do
create :email, email: 'secondary@example.com', user: user
end
@@ -274,99 +263,121 @@ RSpec.describe 'Admin::Users::User' do
expect(page).to have_content("#{secondary_email.email} Verified")
end
end
- end
-
- describe 'show user attributes' do
- it 'has expected attributes', :aggregate_failures do
- visit admin_users_path
- click_link user.name
+ describe 'show user identities' do
+ it 'shows user identities', :aggregate_failures do
+ visit admin_user_identities_path(user)
- expect(page).to have_content 'Account'
- expect(page).to have_content 'Personal projects limit'
+ expect(page).to have_content(user.name)
+ expect(page).to have_content('twitter')
+ end
end
- end
- describe 'remove users secondary email', :js do
- let!(:secondary_email) do
- create :email, email: 'secondary@example.com', user: user
+ describe 'update user identities' do
+ before do
+ allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:twitter, :twitter_updated])
+ end
+
+ it 'modifies twitter identity', :aggregate_failures do
+ visit admin_user_identities_path(user)
+
+ find('.table').find(:link, 'Edit').click
+ fill_in 'identity_extern_uid', with: '654321'
+ select 'twitter_updated', from: 'identity_provider'
+ click_button 'Save changes'
+
+ expect(page).to have_content(user.name)
+ expect(page).to have_content('twitter_updated')
+ expect(page).to have_content('654321')
+ end
end
- it do
- visit admin_user_path(user.username)
+ describe 'remove users secondary email', :js do
+ let_it_be(:secondary_email) do
+ create :email, email: 'secondary@example.com', user: user
+ end
+
+ it do
+ visit admin_user_path(user.username)
- expect(page).to have_content("Secondary email: #{secondary_email.email}")
+ expect(page).to have_content("Secondary email: #{secondary_email.email}")
- accept_confirm { find("#remove_email_#{secondary_email.id}").click }
+ accept_confirm { find("#remove_email_#{secondary_email.id}").click }
- expect(page).not_to have_content(secondary_email.email)
+ expect(page).not_to have_content(secondary_email.email)
+ end
end
- end
- describe 'show user keys', :js do
- it do
- key1 = create(:key, user: user, title: 'ssh-rsa Key1', key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4FIEBXGi4bPU8kzxMefudPIJ08/gNprdNTaO9BR/ndy3+58s2HCTw2xCHcsuBmq+TsAqgEidVq4skpqoTMB+Uot5Uzp9z4764rc48dZiI661izoREoKnuRQSsRqUTHg5wrLzwxlQbl1MVfRWQpqiz/5KjBC7yLEb9AbusjnWBk8wvC1bQPQ1uLAauEA7d836tgaIsym9BrLsMVnR4P1boWD3Xp1B1T/ImJwAGHvRmP/ycIqmKdSpMdJXwxcb40efWVj0Ibbe7ii9eeoLdHACqevUZi6fwfbymdow+FeqlkPoHyGg3Cu4vD/D8+8cRc7mE/zGCWcQ15Var83Tczour Key1')
- key2 = create(:key, user: user, title: 'ssh-rsa Key2', key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQSTWXhJAX/He+nG78MiRRRn7m0Pb0XbcgTxE0etArgoFoh9WtvDf36HG6tOSg/0UUNcp0dICsNAmhBKdncp6cIyPaXJTURPRAGvhI0/VDk4bi27bRnccGbJ/hDaUxZMLhhrzY0r22mjVf8PF6dvv5QUIQVm1/LeaWYsHHvLgiIjwrXirUZPnFrZw6VLREoBKG8uWvfSXw1L5eapmstqfsME8099oi+vWLR8MgEysZQmD28M73fgW4zek6LDQzKQyJx9nB+hJkKUDvcuziZjGmRFlNgSA2mguERwL1OXonD8WYUrBDGKroIvBT39zS5d9tQDnidEJZ9Y8gv5ViYP7x Key2')
+ describe 'remove user with identities' do
+ it 'removes user with twitter identity', :aggregate_failures do
+ visit admin_user_identities_path(user)
- visit admin_users_path
+ click_link 'Delete'
- click_link user.name
- click_link 'SSH keys'
+ expect(page).to have_content(user.name)
+ expect(page).not_to have_content('twitter')
+ end
+ end
- expect(page).to have_content(key1.title)
- expect(page).to have_content(key2.title)
+ describe 'show user keys', :js do
+ it do
+ key1 = create(:key, user: user, title: 'ssh-rsa Key1', key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4FIEBXGi4bPU8kzxMefudPIJ08/gNprdNTaO9BR/ndy3+58s2HCTw2xCHcsuBmq+TsAqgEidVq4skpqoTMB+Uot5Uzp9z4764rc48dZiI661izoREoKnuRQSsRqUTHg5wrLzwxlQbl1MVfRWQpqiz/5KjBC7yLEb9AbusjnWBk8wvC1bQPQ1uLAauEA7d836tgaIsym9BrLsMVnR4P1boWD3Xp1B1T/ImJwAGHvRmP/ycIqmKdSpMdJXwxcb40efWVj0Ibbe7ii9eeoLdHACqevUZi6fwfbymdow+FeqlkPoHyGg3Cu4vD/D8+8cRc7mE/zGCWcQ15Var83Tczour Key1')
+ key2 = create(:key, user: user, title: 'ssh-rsa Key2', key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQSTWXhJAX/He+nG78MiRRRn7m0Pb0XbcgTxE0etArgoFoh9WtvDf36HG6tOSg/0UUNcp0dICsNAmhBKdncp6cIyPaXJTURPRAGvhI0/VDk4bi27bRnccGbJ/hDaUxZMLhhrzY0r22mjVf8PF6dvv5QUIQVm1/LeaWYsHHvLgiIjwrXirUZPnFrZw6VLREoBKG8uWvfSXw1L5eapmstqfsME8099oi+vWLR8MgEysZQmD28M73fgW4zek6LDQzKQyJx9nB+hJkKUDvcuziZjGmRFlNgSA2mguERwL1OXonD8WYUrBDGKroIvBT39zS5d9tQDnidEJZ9Y8gv5ViYP7x Key2')
- click_link key2.title
+ visit admin_user_path(user)
- expect(page).to have_content(key2.title)
- expect(page).to have_content(key2.key)
+ click_link 'SSH keys'
- click_button 'Delete'
+ expect(page).to have_content(key1.title)
+ expect(page).to have_content(key2.title)
- page.within('.modal') do
- page.click_button('Delete')
- end
+ click_link key2.title
- expect(page).not_to have_content(key2.title)
- end
- end
+ expect(page).to have_content(key2.title)
+ expect(page).to have_content(key2.key)
- describe 'show user identities' do
- it 'shows user identities', :aggregate_failures do
- visit admin_user_identities_path(user)
+ click_button 'Delete'
- expect(page).to have_content(user.name)
- expect(page).to have_content('twitter')
- end
- end
+ page.within('.modal') do
+ page.click_button('Delete')
+ end
- describe 'update user identities' do
- before do
- allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:twitter, :twitter_updated])
+ expect(page).not_to have_content(key2.title)
+ end
end
- it 'modifies twitter identity', :aggregate_failures do
- visit admin_user_identities_path(user)
-
- find('.table').find(:link, 'Edit').click
- fill_in 'identity_extern_uid', with: '654321'
- select 'twitter_updated', from: 'identity_provider'
- click_button 'Save changes'
+ describe 'show user attributes' do
+ it 'has expected attributes', :aggregate_failures do
+ visit admin_user_path(user)
- expect(page).to have_content(user.name)
- expect(page).to have_content('twitter_updated')
- expect(page).to have_content('654321')
+ expect(page).to have_content 'Account'
+ expect(page).to have_content 'Personal projects limit'
+ end
end
end
- describe 'remove user with identities' do
- it 'removes user with twitter identity', :aggregate_failures do
- visit admin_user_identities_path(user)
+ [true, false].each do |vue_admin_users|
+ context "with vue_admin_users feature flag set to #{vue_admin_users}", js: vue_admin_users do
+ before do
+ stub_feature_flags(vue_admin_users: vue_admin_users)
+ end
- click_link 'Delete'
+ describe 'GET /admin/users' do
+ context 'user pending approval' do
+ it 'shows user info', :aggregate_failures do
+ user = create(:user, :blocked_pending_approval)
- expect(page).to have_content(user.name)
- expect(page).not_to have_content('twitter')
+ visit admin_users_path
+ click_link 'Pending approval'
+ click_link user.name
+
+ expect(page).to have_content(user.name)
+ expect(page).to have_content('Pending approval')
+ expect(page).to have_link('Approve user')
+ expect(page).to have_link('Reject request')
+ end
+ end
+ end
end
end
end
diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb
index 9482b4f8603..d3931373ee3 100644
--- a/spec/features/admin/users/users_spec.rb
+++ b/spec/features/admin/users/users_spec.rb
@@ -3,298 +3,306 @@
require 'spec_helper'
RSpec.describe 'Admin::Users' do
- include Spec::Support::Helpers::Features::ResponsiveTableHelpers
-
let_it_be(:user, reload: true) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
- let_it_be(:current_user) { create(:admin, last_activity_on: 5.days.ago) }
+ let_it_be(:current_user) { create(:admin) }
before do
sign_in(current_user)
gitlab_enable_admin_mode_sign_in(current_user)
end
- describe 'GET /admin/users' do
- before do
- stub_feature_flags(vue_admin_users: false)
- visit admin_users_path
- end
+ [true, false].each do |vue_admin_users|
+ context "with vue_admin_users feature flag set to #{vue_admin_users}", js: vue_admin_users do
+ before do
+ stub_feature_flags(vue_admin_users: vue_admin_users)
+ end
- it "is ok" do
- expect(current_path).to eq(admin_users_path)
- end
+ describe 'GET /admin/users' do
+ before do
+ visit admin_users_path
+ end
- it "has users list" do
- expect(page).to have_content(current_user.email)
- expect(page).to have_content(current_user.name)
- expect(page).to have_content(current_user.created_at.strftime('%e %b, %Y'))
- expect(page).to have_content(current_user.last_activity_on.strftime('%e %b, %Y'))
- expect(page).to have_content(user.email)
- expect(page).to have_content(user.name)
- expect(page).to have_content('Projects')
- expect(page).to have_button('Block')
- expect(page).to have_button('Deactivate')
- expect(page).to have_button('Delete user')
- expect(page).to have_button('Delete user and contributions')
- end
+ it "is ok" do
+ expect(current_path).to eq(admin_users_path)
+ end
- describe 'view extra user information' do
- it 'shows the user popover on hover', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/11290' do
- expect(page).not_to have_selector('#__BV_popover_1__')
+ it "has users list" do
+ current_user.reload
- first_user_link = page.first('.js-user-link')
- first_user_link.hover
+ expect(page).to have_content(current_user.email)
+ expect(page).to have_content(current_user.name)
+ expect(page).to have_content(current_user.created_at.strftime('%e %b, %Y'))
+ expect(page).to have_content(user.email)
+ expect(page).to have_content(user.name)
+ expect(page).to have_content('Projects')
- expect(page).to have_selector('#__BV_popover_1__')
- end
- end
+ click_user_dropdown_toggle(user.id)
- context 'user project count' do
- before do
- project = create(:project)
- project.add_maintainer(current_user)
- end
+ expect(page).to have_button('Block')
+ expect(page).to have_button('Deactivate')
+ expect(page).to have_button('Delete user')
+ expect(page).to have_button('Delete user and contributions')
+ end
- it 'displays count of users projects' do
- visit admin_users_path
+ it 'clicking edit user takes us to edit page', :aggregate_failures do
+ page.within("[data-testid='user-actions-#{user.id}']") do
+ click_link 'Edit'
+ end
- expect(page.find("[data-testid='user-project-count-#{current_user.id}']").text).to eq("1")
- end
- end
+ expect(page).to have_content('Name')
+ expect(page).to have_content('Password')
+ end
- describe 'tabs' do
- it 'has multiple tabs to filter users' do
- expect(page).to have_link('Active', href: admin_users_path)
- expect(page).to have_link('Admins', href: admin_users_path(filter: 'admins'))
- expect(page).to have_link('2FA Enabled', href: admin_users_path(filter: 'two_factor_enabled'))
- expect(page).to have_link('2FA Disabled', href: admin_users_path(filter: 'two_factor_disabled'))
- expect(page).to have_link('External', href: admin_users_path(filter: 'external'))
- expect(page).to have_link('Blocked', href: admin_users_path(filter: 'blocked'))
- expect(page).to have_link('Deactivated', href: admin_users_path(filter: 'deactivated'))
- expect(page).to have_link('Without projects', href: admin_users_path(filter: 'wop'))
- end
+ describe 'view extra user information' do
+ it 'shows the user popover on hover', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/11290' do
+ expect(page).not_to have_selector('#__BV_popover_1__')
- context '`Pending approval` tab' do
- before do
- visit admin_users_path
- end
+ first_user_link = page.first('.js-user-link')
+ first_user_link.hover
- it 'shows the `Pending approval` tab' do
- expect(page).to have_link('Pending approval', href: admin_users_path(filter: 'blocked_pending_approval'))
+ expect(page).to have_selector('#__BV_popover_1__')
+ end
end
- end
- end
- describe 'search and sort' do
- before_all do
- create(:user, name: 'Foo Bar', last_activity_on: 3.days.ago)
- create(:user, name: 'Foo Baz', last_activity_on: 2.days.ago)
- create(:user, name: 'Dmitriy')
- end
+ context 'user project count' do
+ before do
+ project = create(:project)
+ project.add_maintainer(current_user)
+ end
- it 'searches users by name' do
- visit admin_users_path(search_query: 'Foo')
+ it 'displays count of users projects' do
+ visit admin_users_path
- expect(page).to have_content('Foo Bar')
- expect(page).to have_content('Foo Baz')
- expect(page).not_to have_content('Dmitriy')
- end
+ expect(page.find("[data-testid='user-project-count-#{current_user.id}']").text).to eq("1")
+ end
+ end
- it 'sorts users by name' do
- visit admin_users_path
+ describe 'tabs' do
+ it 'has multiple tabs to filter users' do
+ expect(page).to have_link('Active', href: admin_users_path)
+ expect(page).to have_link('Admins', href: admin_users_path(filter: 'admins'))
+ expect(page).to have_link('2FA Enabled', href: admin_users_path(filter: 'two_factor_enabled'))
+ expect(page).to have_link('2FA Disabled', href: admin_users_path(filter: 'two_factor_disabled'))
+ expect(page).to have_link('External', href: admin_users_path(filter: 'external'))
+ expect(page).to have_link('Blocked', href: admin_users_path(filter: 'blocked'))
+ expect(page).to have_link('Banned', href: admin_users_path(filter: 'banned'))
+ expect(page).to have_link('Deactivated', href: admin_users_path(filter: 'deactivated'))
+ expect(page).to have_link('Without projects', href: admin_users_path(filter: 'wop'))
+ end
+
+ context '`Pending approval` tab' do
+ before do
+ visit admin_users_path
+ end
+
+ it 'shows the `Pending approval` tab' do
+ expect(page).to have_link('Pending approval', href: admin_users_path(filter: 'blocked_pending_approval'))
+ end
+ end
+ end
- sort_by('Name')
+ describe 'search and sort' do
+ before_all do
+ create(:user, name: 'Foo Bar', last_activity_on: 3.days.ago)
+ create(:user, name: 'Foo Baz', last_activity_on: 2.days.ago)
+ create(:user, name: 'Dmitriy')
+ end
- expect(first_row.text).to include('Dmitriy')
- expect(second_row.text).to include('Foo Bar')
- end
+ it 'searches users by name' do
+ visit admin_users_path(search_query: 'Foo')
- it 'sorts search results only' do
- visit admin_users_path(search_query: 'Foo')
+ expect(page).to have_content('Foo Bar')
+ expect(page).to have_content('Foo Baz')
+ expect(page).not_to have_content('Dmitriy')
+ end
- sort_by('Name')
+ it 'sorts users by name' do
+ visit admin_users_path
- expect(page).not_to have_content('Dmitriy')
- expect(first_row.text).to include('Foo Bar')
- expect(second_row.text).to include('Foo Baz')
- end
+ sort_by('Name')
- it 'searches with respect of sorting' do
- visit admin_users_path(sort: 'Name')
+ expect(first_row.text).to include('Dmitriy')
+ expect(second_row.text).to include('Foo Bar')
+ end
- fill_in :search_query, with: 'Foo'
- click_button('Search users')
+ it 'sorts search results only' do
+ visit admin_users_path(search_query: 'Foo')
- expect(first_row.text).to include('Foo Bar')
- expect(second_row.text).to include('Foo Baz')
- end
+ sort_by('Name')
+ expect(page).not_to have_content('Dmitriy')
+ expect(first_row.text).to include('Foo Bar')
+ expect(second_row.text).to include('Foo Baz')
+ end
- it 'sorts users by recent last activity' do
- visit admin_users_path(search_query: 'Foo')
+ it 'searches with respect of sorting' do
+ visit admin_users_path(sort: 'Name')
- sort_by('Recent last activity')
+ fill_in :search_query, with: 'Foo'
+ click_button('Search users')
- expect(first_row.text).to include('Foo Baz')
- expect(second_row.text).to include('Foo Bar')
- end
+ expect(first_row.text).to include('Foo Bar')
+ expect(second_row.text).to include('Foo Baz')
+ end
- it 'sorts users by oldest last activity' do
- visit admin_users_path(search_query: 'Foo')
+ it 'sorts users by recent last activity' do
+ visit admin_users_path(search_query: 'Foo')
- sort_by('Oldest last activity')
+ sort_by('Recent last activity')
- expect(first_row.text).to include('Foo Bar')
- expect(second_row.text).to include('Foo Baz')
- end
- end
+ expect(first_row.text).to include('Foo Baz')
+ expect(second_row.text).to include('Foo Bar')
+ end
- describe 'Two-factor Authentication filters' do
- it 'counts users who have enabled 2FA' do
- create(:user, :two_factor)
+ it 'sorts users by oldest last activity' do
+ visit admin_users_path(search_query: 'Foo')
- visit admin_users_path
+ sort_by('Oldest last activity')
- page.within('.filter-two-factor-enabled small') do
- expect(page).to have_content('1')
+ expect(first_row.text).to include('Foo Bar')
+ expect(second_row.text).to include('Foo Baz')
+ end
end
- end
- it 'filters by users who have enabled 2FA' do
- user = create(:user, :two_factor)
+ describe 'Two-factor Authentication filters' do
+ it 'counts users who have enabled 2FA' do
+ create(:user, :two_factor)
- visit admin_users_path
- click_link '2FA Enabled'
+ visit admin_users_path
- expect(page).to have_content(user.email)
- end
+ page.within('.filter-two-factor-enabled small') do
+ expect(page).to have_content('1')
+ end
+ end
- it 'counts users who have not enabled 2FA' do
- visit admin_users_path
+ it 'filters by users who have enabled 2FA' do
+ user = create(:user, :two_factor)
- page.within('.filter-two-factor-disabled small') do
- expect(page).to have_content('2') # Including admin
- end
- end
+ visit admin_users_path
+ click_link '2FA Enabled'
- it 'filters by users who have not enabled 2FA' do
- visit admin_users_path
- click_link '2FA Disabled'
+ expect(page).to have_content(user.email)
+ end
- expect(page).to have_content(user.email)
- end
- end
+ it 'counts users who have not enabled 2FA' do
+ visit admin_users_path
- describe 'Pending approval filter' do
- it 'counts users who are pending approval' do
- create_list(:user, 2, :blocked_pending_approval)
+ page.within('.filter-two-factor-disabled small') do
+ expect(page).to have_content('2') # Including admin
+ end
+ end
- visit admin_users_path
+ it 'filters by users who have not enabled 2FA' do
+ visit admin_users_path
+ click_link '2FA Disabled'
- page.within('.filter-blocked-pending-approval small') do
- expect(page).to have_content('2')
+ expect(page).to have_content(user.email)
+ end
end
- end
- it 'filters by users who are pending approval' do
- user = create(:user, :blocked_pending_approval)
+ describe 'Pending approval filter' do
+ it 'counts users who are pending approval' do
+ create_list(:user, 2, :blocked_pending_approval)
- visit admin_users_path
- click_link 'Pending approval'
+ visit admin_users_path
- expect(page).to have_content(user.email)
- end
- end
+ page.within('.filter-blocked-pending-approval small') do
+ expect(page).to have_content('2')
+ end
+ end
- context 'when blocking/unblocking a user' do
- it 'shows confirmation and allows blocking and unblocking', :js do
- expect(page).to have_content(user.email)
+ it 'filters by users who are pending approval' do
+ user = create(:user, :blocked_pending_approval)
- click_action_in_user_dropdown(user.id, 'Block')
+ visit admin_users_path
+ click_link 'Pending approval'
- wait_for_requests
+ expect(page).to have_content(user.email)
+ end
+ end
- expect(page).to have_content('Block user')
- expect(page).to have_content('Blocking user has the following effects')
- expect(page).to have_content('User will not be able to login')
- expect(page).to have_content('Owned groups will be left')
+ context 'when blocking/unblocking a user' do
+ it 'shows confirmation and allows blocking and unblocking', :js do
+ expect(page).to have_content(user.email)
- find('.modal-footer button', text: 'Block').click
+ click_action_in_user_dropdown(user.id, 'Block')
- wait_for_requests
+ wait_for_requests
- expect(page).to have_content('Successfully blocked')
- expect(page).not_to have_content(user.email)
+ expect(page).to have_content('Block user')
+ expect(page).to have_content('Blocking user has the following effects')
+ expect(page).to have_content('User will not be able to login')
+ expect(page).to have_content('Owned groups will be left')
- click_link 'Blocked'
+ find('.modal-footer button', text: 'Block').click
- wait_for_requests
+ wait_for_requests
- expect(page).to have_content(user.email)
+ expect(page).to have_content('Successfully blocked')
+ expect(page).not_to have_content(user.email)
- click_action_in_user_dropdown(user.id, 'Unblock')
+ click_link 'Blocked'
- expect(page).to have_content('Unblock user')
- expect(page).to have_content('You can always block their account again if needed.')
+ wait_for_requests
- find('.modal-footer button', text: 'Unblock').click
+ expect(page).to have_content(user.email)
- wait_for_requests
+ click_action_in_user_dropdown(user.id, 'Unblock')
- expect(page).to have_content('Successfully unblocked')
- expect(page).not_to have_content(user.email)
- end
- end
+ expect(page).to have_content('Unblock user')
+ expect(page).to have_content('You can always block their account again if needed.')
- context 'when deactivating/re-activating a user' do
- it 'shows confirmation and allows deactivating and re-activating', :js do
- expect(page).to have_content(user.email)
+ find('.modal-footer button', text: 'Unblock').click
- click_action_in_user_dropdown(user.id, 'Deactivate')
+ wait_for_requests
- expect(page).to have_content('Deactivate user')
- expect(page).to have_content('Deactivating a user has the following effects')
- expect(page).to have_content('The user will be logged out')
- expect(page).to have_content('Personal projects, group and user history will be left intact')
+ expect(page).to have_content('Successfully unblocked')
+ expect(page).not_to have_content(user.email)
+ end
+ end
- find('.modal-footer button', text: 'Deactivate').click
+ context 'when deactivating/re-activating a user' do
+ it 'shows confirmation and allows deactivating and re-activating', :js do
+ expect(page).to have_content(user.email)
- wait_for_requests
+ click_action_in_user_dropdown(user.id, 'Deactivate')
- expect(page).to have_content('Successfully deactivated')
- expect(page).not_to have_content(user.email)
+ expect(page).to have_content('Deactivate user')
+ expect(page).to have_content('Deactivating a user has the following effects')
+ expect(page).to have_content('The user will be logged out')
+ expect(page).to have_content('Personal projects, group and user history will be left intact')
- click_link 'Deactivated'
+ find('.modal-footer button', text: 'Deactivate').click
- wait_for_requests
+ wait_for_requests
- expect(page).to have_content(user.email)
+ expect(page).to have_content('Successfully deactivated')
+ expect(page).not_to have_content(user.email)
- click_action_in_user_dropdown(user.id, 'Activate')
+ click_link 'Deactivated'
- expect(page).to have_content('Activate user')
- expect(page).to have_content('You can always deactivate their account again if needed.')
+ wait_for_requests
- find('.modal-footer button', text: 'Activate').click
+ expect(page).to have_content(user.email)
- wait_for_requests
+ click_action_in_user_dropdown(user.id, 'Activate')
- expect(page).to have_content('Successfully activated')
- expect(page).not_to have_content(user.email)
- end
- end
+ expect(page).to have_content('Activate user')
+ expect(page).to have_content('You can always deactivate their account again if needed.')
- def click_action_in_user_dropdown(user_id, action)
- find("[data-testid='user-action-button-#{user_id}']").click
+ find('.modal-footer button', text: 'Activate').click
- within find("[data-testid='user-action-dropdown-#{user_id}']") do
- find('li button', text: action).click
- end
+ wait_for_requests
- wait_for_requests
+ expect(page).to have_content('Successfully activated')
+ expect(page).not_to have_content(user.email)
+ end
+ end
+ end
end
end
describe 'GET /admin/users/new' do
- let(:user_username) { 'bang' }
+ let_it_be(:user_username) { 'bang' }
before do
visit new_admin_user_path
@@ -344,7 +352,7 @@ RSpec.describe 'Admin::Users' do
end
context 'username contains spaces' do
- let(:user_username) { 'Bing bang' }
+ let_it_be(:user_username) { 'Bing bang' }
it "doesn't create the user and shows an error message" do
expect { click_button 'Create user' }.to change {User.count}.by(0)
@@ -363,22 +371,6 @@ RSpec.describe 'Admin::Users' do
visit new_admin_user_path
end
- def expects_external_to_be_checked
- expect(find('#user_external')).to be_checked
- end
-
- def expects_external_to_be_unchecked
- expect(find('#user_external')).not_to be_checked
- end
-
- def expects_warning_to_be_hidden
- expect(find('#warning_external_automatically_set', visible: :all)[:class]).to include 'hidden'
- end
-
- def expects_warning_to_be_shown
- expect(find('#warning_external_automatically_set')[:class]).not_to include 'hidden'
- end
-
it 'automatically unchecks external for matching email' do
expects_external_to_be_checked
expects_warning_to_be_hidden
@@ -413,55 +405,22 @@ RSpec.describe 'Admin::Users' do
expect(new_user.external).to be_falsy
end
- end
- end
- end
-
- describe 'GET /admin/users/:id/edit' do
- before do
- stub_feature_flags(vue_admin_users: false)
- visit admin_users_path
- click_link "edit_user_#{user.id}"
- end
-
- it 'has user edit page' do
- expect(page).to have_content('Name')
- expect(page).to have_content('Password')
- end
-
- describe 'Update user' do
- before do
- fill_in 'user_name', with: 'Big Bang'
- fill_in 'user_email', with: 'bigbang@mail.com'
- fill_in 'user_password', with: 'AValidPassword1'
- fill_in 'user_password_confirmation', with: 'AValidPassword1'
- choose 'user_access_level_admin'
- click_button 'Save changes'
- end
-
- it 'shows page with new data' do
- expect(page).to have_content('bigbang@mail.com')
- expect(page).to have_content('Big Bang')
- end
- it 'changes user entry' do
- user.reload
- expect(user.name).to eq('Big Bang')
- expect(user.admin?).to be_truthy
- expect(user.password_expires_at).to be <= Time.now
- end
- end
+ def expects_external_to_be_checked
+ expect(find('#user_external')).to be_checked
+ end
- describe 'update username to non ascii char' do
- it do
- fill_in 'user_username', with: '\u3042\u3044'
- click_button('Save')
+ def expects_external_to_be_unchecked
+ expect(find('#user_external')).not_to be_checked
+ end
- page.within '#error_explanation' do
- expect(page).to have_content('Username')
+ def expects_warning_to_be_hidden
+ expect(find('#warning_external_automatically_set', visible: :all)[:class]).to include 'hidden'
end
- expect(page).to have_selector(%(form[action="/admin/users/#{user.username}"]))
+ def expects_warning_to_be_shown
+ expect(find('#warning_external_automatically_set')[:class]).not_to include 'hidden'
+ end
end
end
end
@@ -541,15 +500,108 @@ RSpec.describe 'Admin::Users' do
check_breadcrumb('Edit Identity')
end
+
+ def check_breadcrumb(content)
+ expect(find('.breadcrumbs-sub-title')).to have_content(content)
+ end
end
- def check_breadcrumb(content)
- expect(find('.breadcrumbs-sub-title')).to have_content(content)
+ describe 'GET /admin/users/:id/edit' do
+ before do
+ visit edit_admin_user_path(user)
+ end
+
+ describe 'Update user' do
+ before do
+ fill_in 'user_name', with: 'Big Bang'
+ fill_in 'user_email', with: 'bigbang@mail.com'
+ fill_in 'user_password', with: 'AValidPassword1'
+ fill_in 'user_password_confirmation', with: 'AValidPassword1'
+ choose 'user_access_level_admin'
+ click_button 'Save changes'
+ end
+
+ it 'shows page with new data' do
+ expect(page).to have_content('bigbang@mail.com')
+ expect(page).to have_content('Big Bang')
+ end
+
+ it 'changes user entry' do
+ user.reload
+ expect(user.name).to eq('Big Bang')
+ expect(user.admin?).to be_truthy
+ expect(user.password_expires_at).to be <= Time.now
+ end
+ end
+
+ describe 'update username to non ascii char' do
+ it do
+ fill_in 'user_username', with: '\u3042\u3044'
+ click_button('Save')
+
+ page.within '#error_explanation' do
+ expect(page).to have_content('Username')
+ end
+
+ expect(page).to have_selector(%(form[action="/admin/users/#{user.username}"]))
+ end
+ end
+ end
+
+ # TODO: Move to main GET /admin/users block once feature flag is removed. Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/290737
+ context 'with vue_admin_users feature flag enabled', :js do
+ before do
+ stub_feature_flags(vue_admin_users: true)
+ end
+
+ describe 'GET /admin/users' do
+ context 'user group count', :js do
+ before do
+ group = create(:group)
+ group.add_developer(current_user)
+ project = create(:project, group: create(:group))
+ project.add_reporter(current_user)
+ end
+
+ it 'displays count of the users authorized groups' do
+ visit admin_users_path
+
+ wait_for_requests
+
+ expect(page.find("[data-testid='user-group-count-#{current_user.id}']").text).to eq("2")
+ end
+ end
+ end
end
- def sort_by(text)
- page.within('.user-sort-dropdown') do
- click_link text
+ def click_user_dropdown_toggle(user_id)
+ page.within("[data-testid='user-actions-#{user_id}']") do
+ find("[data-testid='dropdown-toggle']").click
end
end
+
+ def first_row
+ page.all('[role="row"]')[1]
+ end
+
+ def second_row
+ page.all('[role="row"]')[2]
+ end
+
+ def sort_by(option)
+ page.within('.filtered-search-block') do
+ find('.dropdown-menu-toggle').click
+ click_link option
+ end
+ end
+
+ def click_action_in_user_dropdown(user_id, action)
+ click_user_dropdown_toggle(user_id)
+
+ within find("[data-testid='user-actions-#{user_id}']") do
+ find('li button', text: action).click
+ end
+
+ wait_for_requests
+ end
end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index ab544022bff..5d9bb8d8087 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -119,46 +119,21 @@ RSpec.describe 'Project issue boards', :js do
end
context 'search list negation queries' do
- context 'with the NOT queries feature flag disabled' do
- before do
- stub_feature_flags(not_issuable_queries: false)
-
- visit_project_board_path_without_query_limit(project, board)
- end
-
- it 'does not have the != option' do
- find('.filtered-search').set('label:')
-
- wait_for_requests
- within('#js-dropdown-operator') do
- tokens = all(:css, 'li.filter-dropdown-item')
- expect(tokens.count).to eq(1)
- button = tokens[0].find('button')
- expect(button).to have_content('=')
- expect(button).not_to have_content('!=')
- end
- end
+ before do
+ visit_project_board_path_without_query_limit(project, board)
end
- context 'with the NOT queries feature flag enabled' do
- before do
- stub_feature_flags(not_issuable_queries: true)
-
- visit_project_board_path_without_query_limit(project, board)
- end
-
- it 'does not have the != option' do
- find('.filtered-search').set('label:')
+ it 'does not have the != option' do
+ find('.filtered-search').set('label:')
- wait_for_requests
- within('#js-dropdown-operator') do
- tokens = all(:css, 'li.filter-dropdown-item')
- expect(tokens.count).to eq(2)
- button = tokens[0].find('button')
- expect(button).to have_content('=')
- button = tokens[1].find('button')
- expect(button).to have_content('!=')
- end
+ wait_for_requests
+ within('#js-dropdown-operator') do
+ tokens = all(:css, 'li.filter-dropdown-item')
+ expect(tokens.count).to eq(2)
+ button = tokens[0].find('button')
+ expect(button).to have_content('=')
+ button = tokens[1].find('button')
+ expect(button).to have_content('!=')
end
end
end
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index 20ae569322c..129d03d17f3 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -10,6 +10,9 @@ RSpec.describe 'Issue Boards new issue', :js do
let_it_be(:list) { create(:list, board: board, label: label, position: 0) }
let_it_be(:user) { create(:user) }
+ let(:board_list_header) { first('[data-testid="board-list-header"]') }
+ let(:project_select_dropdown) { find('[data-testid="project-select-dropdown"]') }
+
context 'authorized user' do
before do
project.add_maintainer(user)
@@ -24,18 +27,18 @@ RSpec.describe 'Issue Boards new issue', :js do
end
it 'displays new issue button' do
- expect(first('.board')).to have_selector('.issue-count-badge-add-button', count: 1)
+ expect(first('.board')).to have_button('New issue', count: 1)
end
it 'does not display new issue button in closed list' do
page.within('.board:nth-child(3)') do
- expect(page).not_to have_selector('.issue-count-badge-add-button')
+ expect(page).not_to have_button('New issue')
end
end
it 'shows form when clicking button' do
page.within(first('.board')) do
- find('.issue-count-badge-add-button').click
+ click_button 'New issue'
expect(page).to have_selector('.board-new-issue-form')
end
@@ -43,7 +46,7 @@ RSpec.describe 'Issue Boards new issue', :js do
it 'hides form when clicking cancel' do
page.within(first('.board')) do
- find('.issue-count-badge-add-button').click
+ click_button 'New issue'
expect(page).to have_selector('.board-new-issue-form')
@@ -55,7 +58,7 @@ RSpec.describe 'Issue Boards new issue', :js do
it 'creates new issue' do
page.within(first('.board')) do
- find('.issue-count-badge-add-button').click
+ click_button 'New issue'
end
page.within(first('.board-new-issue-form')) do
@@ -80,7 +83,7 @@ RSpec.describe 'Issue Boards new issue', :js do
# TODO https://gitlab.com/gitlab-org/gitlab/-/issues/323446
xit 'shows sidebar when creating new issue' do
page.within(first('.board')) do
- find('.issue-count-badge-add-button').click
+ click_button 'New issue'
end
page.within(first('.board-new-issue-form')) do
@@ -95,7 +98,7 @@ RSpec.describe 'Issue Boards new issue', :js do
it 'successfuly loads labels to be added to newly created issue' do
page.within(first('.board')) do
- find('.issue-count-badge-add-button').click
+ click_button 'New issue'
end
page.within(first('.board-new-issue-form')) do
@@ -109,12 +112,12 @@ RSpec.describe 'Issue Boards new issue', :js do
find('.board-card').click
end
- page.within(first('[data-testid="issue-boards-sidebar"]')) do
- find('.labels [data-testid="edit-button"]').click
+ page.within('[data-testid="sidebar-labels"]') do
+ click_button 'Edit'
wait_for_requests
- expect(page).to have_selector('.labels-select-contents-list .dropdown-content li a')
+ expect(page).to have_content 'Label 1'
end
end
end
@@ -126,70 +129,94 @@ RSpec.describe 'Issue Boards new issue', :js do
end
it 'displays new issue button in open list' do
- expect(first('.board')).to have_selector('.issue-count-badge-add-button', count: 1)
+ expect(first('.board')).to have_button('New issue', count: 1)
end
it 'does not display new issue button in label list' do
page.within('.board:nth-child(2)') do
- expect(page).not_to have_selector('.issue-count-badge-add-button')
+ expect(page).not_to have_button('New issue')
end
end
end
context 'group boards' do
let_it_be(:group) { create(:group, :public) }
- let_it_be(:project) { create(:project, :public, namespace: group) }
+ let_it_be(:project) { create(:project, namespace: group, name: "root project") }
+ let_it_be(:subgroup) { create(:group, parent: group) }
+ let_it_be(:subproject1) { create(:project, group: subgroup, name: "sub project1") }
+ let_it_be(:subproject2) { create(:project, group: subgroup, name: "sub project2") }
let_it_be(:group_board) { create(:board, group: group) }
let_it_be(:project_label) { create(:label, project: project, name: 'label') }
let_it_be(:list) { create(:list, board: group_board, label: project_label, position: 0) }
context 'for unauthorized users' do
- context 'when backlog does not exist' do
- before do
- sign_in(user)
- visit group_board_path(group, group_board)
- wait_for_requests
- end
+ before do
+ visit group_board_path(group, group_board)
+ wait_for_requests
+ end
+ context 'when backlog does not exist' do
it 'does not display new issue button in label list' do
page.within('.board.is-draggable') do
- expect(page).not_to have_selector('.issue-count-badge-add-button')
+ expect(page).not_to have_button('New issue')
end
end
end
context 'when backlog list already exists' do
- let!(:backlog_list) { create(:backlog_list, board: group_board) }
-
- before do
- sign_in(user)
- visit group_board_path(group, group_board)
- wait_for_requests
- end
+ let_it_be(:backlog_list) { create(:backlog_list, board: group_board) }
it 'displays new issue button in open list' do
- expect(first('.board')).to have_selector('.issue-count-badge-add-button', count: 1)
+ expect(first('.board')).to have_button('New issue', count: 1)
end
it 'does not display new issue button in label list' do
page.within('.board.is-draggable') do
- expect(page).not_to have_selector('.issue-count-badge-add-button')
+ expect(page).not_to have_button('New issue')
end
end
end
end
context 'for authorized users' do
- it 'display new issue button in label list' do
- project = create(:project, namespace: group)
+ before do
project.add_reporter(user)
+ subproject1.add_reporter(user)
sign_in(user)
visit group_board_path(group, group_board)
wait_for_requests
+ end
+
+ context 'when backlog does not exist' do
+ it 'display new issue button in label list' do
+ expect(board_list_header).to have_button('New issue')
+ end
+ end
+
+ context 'project select dropdown' do
+ let_it_be(:backlog_list) { create(:backlog_list, board: group_board) }
+
+ before do
+ page.within(board_list_header) do
+ click_button 'New issue'
+ end
+
+ project_select_dropdown.click
+
+ wait_for_requests
+ end
+
+ it 'lists a project which is a direct descendant of the top-level group' do
+ expect(project_select_dropdown).to have_button("root project")
+ end
+
+ it 'lists a project that belongs to a subgroup' do
+ expect(project_select_dropdown).to have_button("sub project1")
+ end
- page.within('.board.is-draggable') do
- expect(page).to have_selector('.issue-count-badge-add-button')
+ it "does not list projects to which user doesn't have access" do
+ expect(project_select_dropdown).not_to have_button("sub project2")
end
end
end
diff --git a/spec/features/boards/sidebar_assignee_spec.rb b/spec/features/boards/sidebar_assignee_spec.rb
index e938612163f..d6adefea6e3 100644
--- a/spec/features/boards/sidebar_assignee_spec.rb
+++ b/spec/features/boards/sidebar_assignee_spec.rb
@@ -18,6 +18,8 @@ RSpec.describe 'Project issue boards sidebar assignee', :js do
let(:card) { find('.board:nth-child(2)').first('.board-card') }
before do
+ stub_licensed_features(multiple_issue_assignees: false)
+
project.add_maintainer(user)
sign_in(user)
@@ -27,10 +29,12 @@ RSpec.describe 'Project issue boards sidebar assignee', :js do
end
context 'assignee' do
+ let(:assignees_widget) { '[data-testid="issue-boards-sidebar"] [data-testid="assignees-widget"]' }
+
it 'updates the issues assignee' do
click_card(card)
- page.within('.assignee') do
+ page.within(assignees_widget) do
click_button('Edit')
wait_for_requests
@@ -41,12 +45,11 @@ RSpec.describe 'Project issue boards sidebar assignee', :js do
first('.gl-avatar-labeled').click
end
- click_button('Apply')
- wait_for_requests
-
expect(page).to have_content(assignee)
end
+ wait_for_requests
+
expect(card).to have_selector('.avatar')
end
@@ -54,7 +57,7 @@ RSpec.describe 'Project issue boards sidebar assignee', :js do
card_two = find('.board:nth-child(2)').find('.board-card:nth-child(2)')
click_card(card_two)
- page.within('.assignee') do
+ page.within(assignees_widget) do
click_button('Edit')
wait_for_requests
@@ -63,9 +66,6 @@ RSpec.describe 'Project issue boards sidebar assignee', :js do
find('[data-testid="unassign"]').click
end
- click_button('Apply')
- wait_for_requests
-
expect(page).to have_content('None')
end
@@ -75,7 +75,7 @@ RSpec.describe 'Project issue boards sidebar assignee', :js do
it 'assignees to current user' do
click_card(card)
- page.within(find('.assignee')) do
+ page.within(assignees_widget) do
expect(page).to have_content('None')
click_button 'assign yourself'
@@ -91,7 +91,7 @@ RSpec.describe 'Project issue boards sidebar assignee', :js do
it 'updates assignee dropdown' do
click_card(card)
- page.within('.assignee') do
+ page.within(assignees_widget) do
click_button('Edit')
wait_for_requests
@@ -102,9 +102,6 @@ RSpec.describe 'Project issue boards sidebar assignee', :js do
first('.gl-avatar-labeled').click
end
- click_button('Apply')
- wait_for_requests
-
expect(page).to have_content(assignee)
end
@@ -112,7 +109,7 @@ RSpec.describe 'Project issue boards sidebar assignee', :js do
find('.board-card:nth-child(2)').click
end
- page.within('.assignee') do
+ page.within(assignees_widget) do
click_button('Edit')
expect(find('.dropdown-menu')).to have_selector('.gl-new-dropdown-item-check-icon')
diff --git a/spec/features/boards/sidebar_labels_in_namespaces_spec.rb b/spec/features/boards/sidebar_labels_in_namespaces_spec.rb
new file mode 100644
index 00000000000..8395a0b33c0
--- /dev/null
+++ b/spec/features/boards/sidebar_labels_in_namespaces_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Issue boards sidebar labels select', :js do
+ include BoardHelpers
+
+ include_context 'labels from nested groups and projects'
+
+ let(:card) { find('.board:nth-child(1)').first('[data-testid="board_card"]') }
+
+ context 'group boards' do
+ context 'in the top-level group board' do
+ let_it_be(:group_board) { create(:board, group: group) }
+ let_it_be(:board_list) { create(:backlog_list, board: group_board) }
+
+ before do
+ load_board group_board_path(group, group_board)
+ end
+
+ context 'selecting an issue from a direct descendant project' do
+ let_it_be(:project_issue) { create(:issue, project: project) }
+
+ include_examples 'an issue from a direct descendant project is selected'
+ end
+
+ context "selecting an issue from a subgroup's project" do
+ let_it_be(:subproject_issue) { create(:issue, project: subproject) }
+
+ include_examples "an issue from a subgroup's project is selected"
+ end
+ end
+ end
+end
diff --git a/spec/features/boards/sub_group_project_spec.rb b/spec/features/boards/sub_group_project_spec.rb
deleted file mode 100644
index bde5f061a67..00000000000
--- a/spec/features/boards/sub_group_project_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Sub-group project issue boards', :js do
- let(:group) { create(:group) }
- let(:nested_group_1) { create(:group, parent: group) }
- let(:project) { create(:project, group: nested_group_1) }
- let(:board) { create(:board, project: project) }
- let(:label) { create(:label, project: project) }
- let(:user) { create(:user) }
- let!(:list1) { create(:list, board: board, label: label, position: 0) }
- let!(:issue) { create(:labeled_issue, project: project, labels: [label]) }
-
- before do
- project.add_maintainer(user)
-
- sign_in(user)
-
- visit project_board_path(project, board)
- wait_for_requests
- end
-
- # TODO https://gitlab.com/gitlab-org/gitlab/-/issues/324290
- xit 'creates new label from sidebar' do
- find('.board-card').click
-
- page.within '.labels' do
- click_link 'Edit'
- click_link 'Create project label'
- end
-
- page.within '.dropdown-new-label' do
- fill_in 'new_label_name', with: 'test label'
- first('.suggest-colors-dropdown a').click
-
- click_button 'Create'
-
- wait_for_requests
- end
-
- page.within '.labels' do
- expect(page).to have_link 'test label'
- end
- end
-end
diff --git a/spec/features/boards/user_visits_board_spec.rb b/spec/features/boards/user_visits_board_spec.rb
new file mode 100644
index 00000000000..7fe32557d6a
--- /dev/null
+++ b/spec/features/boards/user_visits_board_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User visits issue boards', :js do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:group) { create_default(:group, :public) }
+ let_it_be(:project) { create_default(:project, :public, group: group) }
+
+ # TODO use 'let' when rspec-parameterized supports it.
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/329746
+ label_name1 = 'foobar'
+ label_name2 = 'in dev'
+ assignee_username = 'root'
+ issue_with_label1 = "issue with label1"
+ issue_with_label2 = "issue with label2"
+ issue_with_assignee = "issue with assignee"
+ issue_with_milestone = "issue with milestone"
+ issue_with_all_filters = "issue with all filters"
+
+ let_it_be(:label1) { create(:group_label, group: group, name: label_name1) }
+ let_it_be(:label2) { create(:group_label, group: group, name: label_name2) }
+ let_it_be(:assignee) { create_default(:group_member, :maintainer, user: create(:user, username: assignee_username), group: group ).user }
+ let_it_be(:milestone) { create_default(:milestone, project: project, start_date: Date.today - 1, due_date: 7.days.from_now) }
+
+ before_all do
+ create_default(:issue, project: project, title: issue_with_label1, labels: [label1])
+ create_default(:issue, project: project, title: issue_with_label2, labels: [label2])
+ create_default(:issue, project: project, title: issue_with_assignee, assignees: [assignee])
+ create_default(:issue, project: project, title: issue_with_milestone, milestone: milestone)
+ create_default(:issue, project: project, title: issue_with_all_filters, labels: [label1, label2], assignees: [assignee], milestone: milestone)
+ end
+
+ shared_examples "visiting board path with search params" do
+ where(:params, :expected_issues) do
+ { "label_name" => [label_name1] } | [issue_with_label1, issue_with_all_filters]
+ { "label_name" => [label_name2] } | [issue_with_label2, issue_with_all_filters]
+ { "label_name" => [label_name1, label_name2] } | [issue_with_all_filters]
+ { "assignee_username" => assignee_username } | [issue_with_assignee, issue_with_all_filters]
+ { "milestone_title" => '#started' } | [issue_with_milestone, issue_with_all_filters]
+ { "label_name" => [label_name1, label_name2], "assignee_username" => assignee_username } | [issue_with_all_filters]
+ end
+
+ with_them do
+ before do
+ visit board_path
+
+ wait_for_requests
+ end
+
+ it 'displays all issues satisfiying filter params and correctly sets url params' do
+ expect(page).to have_current_path(board_path)
+
+ page.assert_selector('[data-testid="board_card"]', count: expected_issues.length)
+ expected_issues.each { |issue_title| expect(page).to have_link issue_title }
+ end
+ end
+ end
+
+ context "project boards" do
+ let_it_be(:board) { create_default(:board, project: project) }
+ let_it_be(:backlog_list) { create_default(:backlog_list, board: board) }
+
+ let(:board_path) { project_boards_path(project, params) }
+
+ include_examples "visiting board path with search params"
+ end
+
+ context "group boards" do
+ let_it_be(:board) { create_default(:board, group: group) }
+ let_it_be(:backlog_list) { create_default(:backlog_list, board: board) }
+
+ let(:board_path) { group_boards_path(group, params) }
+
+ include_examples 'visiting board path with search params'
+ end
+end
diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb
index 0b73226268d..1281d890ef7 100644
--- a/spec/features/calendar_spec.rb
+++ b/spec/features/calendar_spec.rb
@@ -146,7 +146,7 @@ RSpec.describe 'Contributions Calendar', :js do
describe '1 issue creation calendar activity' do
before do
- Issues::CreateService.new(contributed_project, user, issue_params).execute
+ Issues::CreateService.new(project: contributed_project, current_user: user, params: issue_params).execute
end
it_behaves_like 'a day with activity', contribution_count: 1
@@ -181,7 +181,7 @@ RSpec.describe 'Contributions Calendar', :js do
push_code_contribution
travel_to(Date.yesterday) do
- Issues::CreateService.new(contributed_project, user, issue_params).execute
+ Issues::CreateService.new(project: contributed_project, current_user: user, params: issue_params).execute
end
end
include_context 'visit user page'
diff --git a/spec/features/dashboard/active_tab_spec.rb b/spec/features/dashboard/active_tab_spec.rb
index 3a532cb4161..a1fb0beda70 100644
--- a/spec/features/dashboard/active_tab_spec.rb
+++ b/spec/features/dashboard/active_tab_spec.rb
@@ -3,30 +3,56 @@
require 'spec_helper'
RSpec.describe 'Dashboard Active Tab', :js do
- before do
- stub_feature_flags(combined_menu: false)
+ shared_examples 'combined_menu: feature flag examples' do
+ before do
+ sign_in(create(:user))
+ end
- sign_in(create(:user))
- end
+ shared_examples 'page has active tab' do |title|
+ it "#{title} tab" do
+ pending_on_combined_menu_flag
+
+ subject
- shared_examples 'page has active tab' do |title|
- it "#{title} tab" do
- subject
+ expect(page).to have_selector('.navbar-sub-nav li.active', count: 1)
+ expect(find('.navbar-sub-nav li.active')).to have_content(title)
+ end
+ end
+
+ context 'on dashboard projects' do
+ it_behaves_like 'page has active tab', 'Projects' do
+ subject { visit dashboard_projects_path }
+ end
+ end
- expect(page).to have_selector('.navbar-sub-nav li.active', count: 1)
- expect(find('.navbar-sub-nav li.active')).to have_content(title)
+ context 'on dashboard groups' do
+ it_behaves_like 'page has active tab', 'Groups' do
+ subject { visit dashboard_groups_path }
+ end
end
end
- context 'on dashboard projects' do
- it_behaves_like 'page has active tab', 'Projects' do
- subject { visit dashboard_projects_path }
+ context 'with combined_menu: feature flag on' do
+ let(:needs_rewrite_for_combined_menu_flag_on) { true }
+
+ before do
+ stub_feature_flags(combined_menu: true)
end
+
+ it_behaves_like 'combined_menu: feature flag examples'
end
- context 'on dashboard groups' do
- it_behaves_like 'page has active tab', 'Groups' do
- subject { visit dashboard_groups_path }
+ context 'with combined_menu feature flag off' do
+ let(:needs_rewrite_for_combined_menu_flag_on) { false }
+
+ before do
+ stub_feature_flags(combined_menu: false)
end
+
+ it_behaves_like 'combined_menu: feature flag examples'
+ end
+
+ def pending_on_combined_menu_flag
+ pending 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587' if needs_rewrite_for_combined_menu_flag_on
end
end
diff --git a/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb b/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb
index 179d9d09905..0620f819332 100644
--- a/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb
+++ b/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb
@@ -7,36 +7,64 @@ RSpec.describe 'The group dashboard' do
let(:user) { create(:user) }
- before do
- stub_feature_flags(combined_menu: false)
+ shared_examples 'combined_menu: feature flag examples' do
+ before do
+ sign_in user
+ end
- sign_in user
- end
+ describe 'The top navigation' do
+ it 'has all the expected links' do
+ pending_on_combined_menu_flag
- describe 'The top navigation' do
- it 'has all the expected links' do
- visit dashboard_groups_path
+ visit dashboard_groups_path
- within('.navbar') do
- expect(page).to have_button('Projects')
- expect(page).to have_button('Groups')
- expect(page).to have_link('Activity')
- expect(page).to have_link('Milestones')
- expect(page).to have_link('Snippets')
+ within('.navbar') do
+ expect(page).to have_button('Projects')
+ expect(page).to have_button('Groups')
+ expect(page).to have_link('Activity')
+ expect(page).to have_link('Milestones')
+ expect(page).to have_link('Snippets')
+ end
end
- end
- it 'hides some links when an external authorization service is enabled' do
- enable_external_authorization_service_check
- visit dashboard_groups_path
+ it 'hides some links when an external authorization service is enabled' do
+ pending_on_combined_menu_flag
+
+ enable_external_authorization_service_check
+ visit dashboard_groups_path
- within('.navbar') do
- expect(page).to have_button('Projects')
- expect(page).to have_button('Groups')
- expect(page).not_to have_link('Activity')
- expect(page).not_to have_link('Milestones')
- expect(page).to have_link('Snippets')
+ within('.navbar') do
+ expect(page).to have_button('Projects')
+ expect(page).to have_button('Groups')
+ expect(page).not_to have_link('Activity')
+ expect(page).not_to have_link('Milestones')
+ expect(page).to have_link('Snippets')
+ end
end
end
end
+
+ context 'with combined_menu: feature flag on' do
+ let(:needs_rewrite_for_combined_menu_flag_on) { true }
+
+ before do
+ stub_feature_flags(combined_menu: true)
+ end
+
+ it_behaves_like 'combined_menu: feature flag examples'
+ end
+
+ context 'with combined_menu feature flag off' do
+ let(:needs_rewrite_for_combined_menu_flag_on) { false }
+
+ before do
+ stub_feature_flags(combined_menu: false)
+ end
+
+ it_behaves_like 'combined_menu: feature flag examples'
+ end
+
+ def pending_on_combined_menu_flag
+ pending 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587' if needs_rewrite_for_combined_menu_flag_on
+ end
end
diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb
index e96a60b2ab2..5f60832dbc9 100644
--- a/spec/features/dashboard/shortcuts_spec.rb
+++ b/spec/features/dashboard/shortcuts_spec.rb
@@ -3,71 +3,97 @@
require 'spec_helper'
RSpec.describe 'Dashboard shortcuts', :js do
- before do
- stub_feature_flags(combined_menu: false)
- end
+ shared_examples 'combined_menu: feature flag examples' do
+ context 'logged in' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
- context 'logged in' do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ visit root_dashboard_path
+ end
- before do
- project.add_developer(user)
- sign_in(user)
- visit root_dashboard_path
- end
+ it 'navigate to tabs' do
+ pending_on_combined_menu_flag
- it 'navigate to tabs' do
- find('body').send_keys([:shift, 'I'])
+ find('body').send_keys([:shift, 'I'])
- check_page_title('Issues')
+ check_page_title('Issues')
- find('body').send_keys([:shift, 'M'])
+ find('body').send_keys([:shift, 'M'])
- check_page_title('Merge requests')
+ check_page_title('Merge requests')
- find('body').send_keys([:shift, 'T'])
+ find('body').send_keys([:shift, 'T'])
- check_page_title('To-Do List')
+ check_page_title('To-Do List')
- find('body').send_keys([:shift, 'G'])
+ find('body').send_keys([:shift, 'G'])
- check_page_title('Groups')
+ check_page_title('Groups')
- find('body').send_keys([:shift, 'P'])
+ find('body').send_keys([:shift, 'P'])
- check_page_title('Projects')
+ check_page_title('Projects')
- find('body').send_keys([:shift, 'A'])
+ find('body').send_keys([:shift, 'A'])
- check_page_title('Activity')
+ check_page_title('Activity')
+ end
end
- end
- context 'logged out' do
- before do
- visit explore_root_path
+ context 'logged out' do
+ before do
+ visit explore_root_path
+ end
+
+ it 'navigate to tabs' do
+ pending_on_combined_menu_flag
+
+ find('body').send_keys([:shift, 'G'])
+
+ find('.nothing-here-block')
+ expect(page).to have_content('No public groups')
+
+ find('body').send_keys([:shift, 'S'])
+
+ find('.nothing-here-block')
+ expect(page).to have_content('No snippets found')
+
+ find('body').send_keys([:shift, 'P'])
+
+ find('.nothing-here-block')
+ expect(page).to have_content('Explore public groups to find projects to contribute to.')
+ end
end
- it 'navigate to tabs' do
- find('body').send_keys([:shift, 'G'])
+ def check_page_title(title)
+ expect(find('.page-title')).to have_content(title)
+ end
+ end
- find('.nothing-here-block')
- expect(page).to have_content('No public groups')
+ context 'with combined_menu: feature flag on' do
+ let(:needs_rewrite_for_combined_menu_flag_on) { true }
- find('body').send_keys([:shift, 'S'])
+ before do
+ stub_feature_flags(combined_menu: true)
+ end
- find('.nothing-here-block')
- expect(page).to have_content('No snippets found')
+ it_behaves_like 'combined_menu: feature flag examples'
+ end
- find('body').send_keys([:shift, 'P'])
+ context 'with combined_menu feature flag off' do
+ let(:needs_rewrite_for_combined_menu_flag_on) { false }
- find('.nothing-here-block')
- expect(page).to have_content('Explore public groups to find projects to contribute to.')
+ before do
+ stub_feature_flags(combined_menu: false)
end
+
+ it_behaves_like 'combined_menu: feature flag examples'
end
- def check_page_title(title)
- expect(find('.page-title')).to have_content(title)
+ def pending_on_combined_menu_flag
+ pending 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587' if needs_rewrite_for_combined_menu_flag_on
end
end
diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb
index 0b4fed55f11..0bc6cc9c017 100644
--- a/spec/features/dashboard/todos/todos_spec.rb
+++ b/spec/features/dashboard/todos/todos_spec.rb
@@ -79,7 +79,7 @@ RSpec.describe 'Dashboard Todos' do
end
it 'has not "All done" message' do
- expect(page).not_to have_selector('.todos-all-done')
+ expect(page).not_to have_selector('.empty-state')
end
end
diff --git a/spec/features/frequently_visited_projects_and_groups_spec.rb b/spec/features/frequently_visited_projects_and_groups_spec.rb
index 6c25afdf6d4..9110c7ad65a 100644
--- a/spec/features/frequently_visited_projects_and_groups_spec.rb
+++ b/spec/features/frequently_visited_projects_and_groups_spec.rb
@@ -5,45 +5,73 @@ require 'spec_helper'
RSpec.describe 'Frequently visited items', :js do
let_it_be(:user) { create(:user) }
- before do
- stub_feature_flags(combined_menu: false)
+ shared_examples 'combined_menu: feature flag examples' do
+ before do
+ sign_in(user)
+ end
- sign_in(user)
- end
+ context 'for projects' do
+ let_it_be(:project) { create(:project, :public) }
- context 'for projects' do
- let_it_be(:project) { create(:project, :public) }
+ it 'increments localStorage counter when visiting the project' do
+ pending_on_combined_menu_flag
- it 'increments localStorage counter when visiting the project' do
- visit project_path(project)
+ visit project_path(project)
- frequent_projects = nil
+ frequent_projects = nil
- wait_for('localStorage frequent-projects') do
- frequent_projects = page.evaluate_script("localStorage['#{user.username}/frequent-projects']")
+ wait_for('localStorage frequent-projects') do
+ frequent_projects = page.evaluate_script("localStorage['#{user.username}/frequent-projects']")
- frequent_projects.present?
- end
+ frequent_projects.present?
+ end
- expect(Gitlab::Json.parse(frequent_projects)).to contain_exactly(a_hash_including('id' => project.id, 'frequency' => 1))
+ expect(Gitlab::Json.parse(frequent_projects)).to contain_exactly(a_hash_including('id' => project.id, 'frequency' => 1))
+ end
end
- end
- context 'for groups' do
- let_it_be(:group) { create(:group, :public) }
+ context 'for groups' do
+ let_it_be(:group) { create(:group, :public) }
- it 'increments localStorage counter when visiting the group' do
- visit group_path(group)
+ it 'increments localStorage counter when visiting the group' do
+ pending_on_combined_menu_flag
- frequent_groups = nil
+ visit group_path(group)
- wait_for('localStorage frequent-groups') do
- frequent_groups = page.evaluate_script("localStorage['#{user.username}/frequent-groups']")
+ frequent_groups = nil
- frequent_groups.present?
+ wait_for('localStorage frequent-groups') do
+ frequent_groups = page.evaluate_script("localStorage['#{user.username}/frequent-groups']")
+
+ frequent_groups.present?
+ end
+
+ expect(Gitlab::Json.parse(frequent_groups)).to contain_exactly(a_hash_including('id' => group.id, 'frequency' => 1))
end
+ end
+ end
- expect(Gitlab::Json.parse(frequent_groups)).to contain_exactly(a_hash_including('id' => group.id, 'frequency' => 1))
+ context 'with combined_menu: feature flag on' do
+ let(:needs_rewrite_for_combined_menu_flag_on) { true }
+
+ before do
+ stub_feature_flags(combined_menu: true)
end
+
+ it_behaves_like 'combined_menu: feature flag examples'
+ end
+
+ context 'with combined_menu feature flag off' do
+ let(:needs_rewrite_for_combined_menu_flag_on) { false }
+
+ before do
+ stub_feature_flags(combined_menu: false)
+ end
+
+ it_behaves_like 'combined_menu: feature flag examples'
+ end
+
+ def pending_on_combined_menu_flag
+ pending 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587' if needs_rewrite_for_combined_menu_flag_on
end
end
diff --git a/spec/features/groups/group_page_with_external_authorization_service_spec.rb b/spec/features/groups/group_page_with_external_authorization_service_spec.rb
index 187d878472e..59a7feb813b 100644
--- a/spec/features/groups/group_page_with_external_authorization_service_spec.rb
+++ b/spec/features/groups/group_page_with_external_authorization_service_spec.rb
@@ -15,8 +15,7 @@ RSpec.describe 'The group page' do
def expect_all_sidebar_links
within('.nav-sidebar') do
- expect(page).to have_link('Group overview')
- expect(page).to have_link('Details')
+ expect(page).to have_link('Group information')
expect(page).to have_link('Activity')
expect(page).to have_link('Issues')
expect(page).to have_link('Merge requests')
@@ -44,8 +43,7 @@ RSpec.describe 'The group page' do
visit group_path(group)
within('.nav-sidebar') do
- expect(page).to have_link('Group overview')
- expect(page).to have_link('Details')
+ expect(page).to have_link('Group information')
expect(page).not_to have_link('Activity')
expect(page).not_to have_link('Contribution')
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index b0d2f90145f..21b39d2da46 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -217,7 +217,7 @@ RSpec.describe 'Group issues page' do
it 'first pagination item is active' do
page.within('.gl-pagination') do
- expect(find('.active')).to have_content('1')
+ expect(find('li.active')).to have_content('1')
end
end
end
diff --git a/spec/features/groups/members/manage_groups_spec.rb b/spec/features/groups/members/manage_groups_spec.rb
index e9bbe9de3c9..40cd54c1e33 100644
--- a/spec/features/groups/members/manage_groups_spec.rb
+++ b/spec/features/groups/members/manage_groups_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe 'Groups > Members > Manage groups', :js do
include Select2Helper
include Spec::Support::Helpers::Features::MembersHelpers
+ include Spec::Support::Helpers::Features::InviteMembersModalHelper
let_it_be(:user) { create(:user) }
@@ -12,18 +13,43 @@ RSpec.describe 'Groups > Members > Manage groups', :js do
sign_in(user)
end
- context 'when group link does not exist' do
- let_it_be(:group) { create(:group) }
- let_it_be(:group_to_add) { create(:group) }
-
+ context 'with invite_members_group_modal disabled' do
before do
stub_feature_flags(invite_members_group_modal: false)
- group.add_owner(user)
- visit group_group_members_path(group)
end
- it 'add group to group' do
- add_group(group_to_add.id, 'Reporter')
+ context 'when group link does not exist' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:group_to_add) { create(:group) }
+
+ before do
+ group.add_owner(user)
+ group_to_add.add_owner(user)
+ visit group_group_members_path(group)
+ end
+
+ it 'can share group with group' do
+ add_group(group_to_add.id, 'Reporter')
+
+ click_groups_tab
+
+ page.within(first_row) do
+ expect(page).to have_content(group_to_add.name)
+ expect(page).to have_content('Reporter')
+ end
+ end
+ end
+ end
+
+ context 'when group link does not exist' do
+ it 'can share a group with group' do
+ group = create(:group)
+ group_to_add = create(:group)
+ group.add_owner(user)
+ group_to_add.add_owner(user)
+
+ visit group_group_members_path(group)
+ invite_group(group_to_add.name, role: 'Reporter')
click_groups_tab
diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb
index 3b637a10abe..c5e6479ec51 100644
--- a/spec/features/groups/members/manage_members_spec.rb
+++ b/spec/features/groups/members/manage_members_spec.rb
@@ -5,13 +5,13 @@ require 'spec_helper'
RSpec.describe 'Groups > Members > Manage members' do
include Select2Helper
include Spec::Support::Helpers::Features::MembersHelpers
+ include Spec::Support::Helpers::Features::InviteMembersModalHelper
let(:user1) { create(:user, name: 'John Doe') }
let(:user2) { create(:user, name: 'Mary Jane') }
let(:group) { create(:group) }
before do
- stub_feature_flags(invite_members_group_modal: false)
sign_in(user1)
end
@@ -26,16 +26,28 @@ RSpec.describe 'Groups > Members > Manage members' do
end
end
- context 'when Invite Members modal is enabled' do
- before do
- stub_feature_flags(invite_members_group_modal: true)
+ shared_examples 'does not include either invite modal or either invite form' do
+ it 'does not include either of the invite members or invite group modal buttons' do
+ expect(page).not_to have_selector '.js-invite-members-modal'
+ expect(page).not_to have_selector '.js-invite-group-modal'
end
+ it 'does not include either of the invite users or invite group forms' do
+ expect(page).not_to have_selector '.invite-users-form'
+ expect(page).not_to have_selector '.invite-group-form'
+ end
+ end
+
+ context 'when Invite Members modal is enabled' do
it_behaves_like 'includes the correct Invite link', '.js-invite-members-trigger', '.invite-users-form'
it_behaves_like 'includes the correct Invite link', '.js-invite-group-trigger', '.invite-group-form'
end
context 'when Invite Members modal is disabled' do
+ before do
+ stub_feature_flags(invite_members_group_modal: false)
+ end
+
it_behaves_like 'includes the correct Invite link', '.invite-users-form', '.js-invite-members-trigger'
it_behaves_like 'includes the correct Invite link', '.invite-group-form', '.js-invite-group-trigger'
end
@@ -59,7 +71,7 @@ RSpec.describe 'Groups > Members > Manage members' do
visit group_group_members_path(group)
- add_user(user2.id, 'Reporter')
+ invite_member(user2.name, role: 'Reporter')
page.within(second_row) do
expect(page).to have_content(user2.name)
@@ -73,21 +85,46 @@ RSpec.describe 'Groups > Members > Manage members' do
visit group_group_members_path(group)
- find('.select2-container').click
- select_input = find('.select2-input')
+ click_on 'Invite members'
+ fill_in 'Select members or type email addresses', with: '@gitlab.com'
- select_input.send_keys('@gitlab.com')
wait_for_requests
expect(page).to have_content('No matches found')
- select_input.native.clear
- select_input.send_keys('undisclosed_email@gitlab.com')
+ fill_in 'Select members or type email addresses', with: 'undisclosed_email@gitlab.com'
wait_for_requests
expect(page).to have_content("Jane 'invisible' Doe")
end
+ context 'when Invite Members modal is disabled' do
+ before do
+ stub_feature_flags(invite_members_group_modal: false)
+ end
+
+ it 'do not disclose email addresses', :js do
+ group.add_owner(user1)
+ create(:user, email: 'undisclosed_email@gitlab.com', name: "Jane 'invisible' Doe")
+
+ visit group_group_members_path(group)
+
+ find('.select2-container').click
+ select_input = find('.select2-input')
+
+ select_input.send_keys('@gitlab.com')
+ wait_for_requests
+
+ expect(page).to have_content('No matches found')
+
+ select_input.native.clear
+ select_input.send_keys('undisclosed_email@gitlab.com')
+ wait_for_requests
+
+ expect(page).to have_content("Jane 'invisible' Doe")
+ end
+ end
+
it 'remove user from group', :js do
group.add_owner(user1)
group.add_developer(user2)
@@ -115,7 +152,7 @@ RSpec.describe 'Groups > Members > Manage members' do
visit group_group_members_path(group)
- add_user(user1.id, 'Reporter')
+ invite_member(user1.name, role: 'Reporter')
page.within(first_row) do
expect(page).to have_content(user1.name)
@@ -128,7 +165,7 @@ RSpec.describe 'Groups > Members > Manage members' do
visit group_group_members_path(group)
- add_user('test@example.com', 'Reporter')
+ invite_member('test@example.com', role: 'Reporter')
expect(page).to have_link 'Invited'
click_link 'Invited'
@@ -140,29 +177,46 @@ RSpec.describe 'Groups > Members > Manage members' do
end
end
- it 'guest can not manage other users', :js do
- group.add_guest(user1)
- group.add_developer(user2)
+ context 'as a guest', :js do
+ before do
+ group.add_guest(user1)
+ group.add_developer(user2)
- visit group_group_members_path(group)
+ visit group_group_members_path(group)
+ end
- expect(page).not_to have_selector '.invite-users-form'
- expect(page).not_to have_selector '.invite-group-form'
+ it_behaves_like 'does not include either invite modal or either invite form'
- page.within(second_row) do
- # Can not modify user2 role
- expect(page).not_to have_button 'Developer'
+ it 'does not include a button on the members page list to manage or remove the existing member', :js do
+ page.within(second_row) do
+ # Can not modify user2 role
+ expect(page).not_to have_button 'Developer'
- # Can not remove user2
- expect(page).not_to have_selector 'button[title="Remove member"]'
+ # Can not remove user2
+ expect(page).not_to have_selector 'button[title="Remove member"]'
+ end
end
end
- def add_user(id, role)
- page.within ".invite-users-form" do
- select2(id, from: "#user_ids", multiple: true)
- select(role, from: "access_level")
- click_button "Invite"
+ context 'As a guest when the :invite_members_group_modal feature flag is disabled', :js do
+ before do
+ stub_feature_flags(invite_members_group_modal: false)
+ group.add_guest(user1)
+ group.add_developer(user2)
+
+ visit group_group_members_path(group)
+ end
+
+ it_behaves_like 'does not include either invite modal or either invite form'
+
+ it 'does not include a button on the members page list to manage or remove the existing member', :js do
+ page.within(second_row) do
+ # Can not modify user2 role
+ expect(page).not_to have_button 'Developer'
+
+ # Can not remove user2
+ expect(page).not_to have_selector 'button[title="Remove member"]'
+ end
end
end
end
diff --git a/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb
index d31a7977f66..ddf3c6d8f9b 100644
--- a/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb
+++ b/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js do
- include Select2Helper
include Spec::Support::Helpers::Features::MembersHelpers
+ include Spec::Support::Helpers::Features::InviteMembersModalHelper
let_it_be(:user1) { create(:user, name: 'John Doe') }
let_it_be(:group) { create(:group) }
@@ -12,7 +12,6 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js
let(:new_member) { create(:user, name: 'Mary Jane') }
before do
- stub_feature_flags(invite_members_group_modal: false)
group.add_owner(user1)
sign_in(user1)
end
@@ -20,14 +19,7 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js
it 'expiration date is displayed in the members list' do
visit group_group_members_path(group)
- page.within invite_users_form do
- select2(new_member.id, from: '#user_ids', multiple: true)
-
- fill_in 'expires_at', with: 5.days.from_now.to_date
- find_field('expires_at').native.send_keys :enter
-
- click_on 'Invite'
- end
+ invite_member(new_member.name, role: 'Guest', expires_at: 5.days.from_now.to_date)
page.within second_row do
expect(page).to have_content(/in \d days/)
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index 1d9ac5ee1e9..c51ee250331 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -54,11 +54,11 @@ RSpec.describe 'Group milestones' do
expect(find('.start_date')).to have_content(Date.today.at_beginning_of_month.strftime('%b %-d, %Y'))
end
- it 'description input does not support autocomplete' do
+ it 'description input support autocomplete' do
description = find('.note-textarea')
description.native.send_keys('!')
- expect(page).not_to have_selector('.atwho-view')
+ expect(page).to have_selector('.atwho-view')
end
end
diff --git a/spec/features/groups/milestones/gfm_autocomplete_spec.rb b/spec/features/groups/milestones/gfm_autocomplete_spec.rb
new file mode 100644
index 00000000000..85a14123294
--- /dev/null
+++ b/spec/features/groups/milestones/gfm_autocomplete_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'GFM autocomplete', :js do
+ let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
+ let_it_be(:group) { create(:group, name: 'Ancestor') }
+ let_it_be(:project) { create(:project, :repository, group: group) }
+ let_it_be(:issue) { create(:issue, project: project, assignees: [user], title: 'My special issue') }
+ let_it_be(:label) { create(:group_label, group: group, title: 'special+') }
+ let_it_be(:milestone) { create(:milestone, resource_parent: group, title: "group milestone") }
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
+
+ shared_examples 'displays autocomplete menu for all entities' do
+ it 'autocompletes all available entities' do
+ fill_in 'Description', with: User.reference_prefix
+ wait_for_requests
+ expect(find_autocomplete_menu).to be_visible
+ expect_autocomplete_entry(group.name)
+
+ fill_in 'Description', with: Label.reference_prefix
+ wait_for_requests
+ expect(find_autocomplete_menu).to be_visible
+ expect_autocomplete_entry(label.title)
+
+ fill_in 'Description', with: Milestone.reference_prefix
+ wait_for_requests
+ expect(find_autocomplete_menu).to be_visible
+ expect_autocomplete_entry(milestone.title)
+
+ fill_in 'Description', with: Issue.reference_prefix
+ wait_for_requests
+ expect(find_autocomplete_menu).to be_visible
+ expect_autocomplete_entry(issue.title)
+
+ fill_in 'Description', with: MergeRequest.reference_prefix
+ wait_for_requests
+ expect(find_autocomplete_menu).to be_visible
+ expect_autocomplete_entry(merge_request.title)
+ end
+ end
+
+ before_all do
+ group.add_maintainer(user)
+ end
+
+ describe 'new milestone page' do
+ before do
+ sign_in(user)
+ visit new_group_milestone_path(group)
+
+ wait_for_requests
+ end
+
+ it_behaves_like 'displays autocomplete menu for all entities'
+ end
+
+ describe 'update milestone page' do
+ before do
+ sign_in(user)
+ visit edit_group_milestone_path(group, milestone)
+
+ wait_for_requests
+ end
+
+ it_behaves_like 'displays autocomplete menu for all entities'
+ end
+
+ private
+
+ def find_autocomplete_menu
+ find('.atwho-view ul', visible: true)
+ end
+
+ def expect_autocomplete_entry(entry)
+ page.within('.atwho-container') do
+ expect(page).to have_content(entry)
+ end
+ end
+end
diff --git a/spec/features/groups/navbar_spec.rb b/spec/features/groups/navbar_spec.rb
index 021b1af54d4..b46d4dae87a 100644
--- a/spec/features/groups/navbar_spec.rb
+++ b/spec/features/groups/navbar_spec.rb
@@ -13,21 +13,10 @@ RSpec.describe 'Group navbar' do
let(:structure) do
[
- {
- nav_item: _('Group overview'),
- nav_sub_items: [
- _('Details'),
- _('Activity')
- ]
- },
+ group_information_nav_item,
{
nav_item: _('Issues'),
- nav_sub_items: [
- _('List'),
- _('Board'),
- _('Labels'),
- _('Milestones')
- ]
+ nav_sub_items: issues_nav_items
},
{
nav_item: _('Merge requests'),
@@ -40,11 +29,12 @@ RSpec.describe 'Group navbar' do
nav_sub_items: []
},
(analytics_nav_item if Gitlab.ee?),
- {
- nav_item: _('Members'),
- nav_sub_items: []
- }
- ]
+ members_nav_item
+ ].compact
+ end
+
+ let(:members_nav_item) do
+ nil
end
before do
@@ -87,4 +77,40 @@ RSpec.describe 'Group navbar' do
it_behaves_like 'verified navigation bar'
end
+
+ context 'when feature flag :sidebar_refactor is disabled' do
+ let(:group_information_nav_item) do
+ {
+ nav_item: _('Group overview'),
+ nav_sub_items: [
+ _('Details'),
+ _('Activity')
+ ]
+ }
+ end
+
+ let(:members_nav_item) do
+ {
+ nav_item: _('Members'),
+ nav_sub_items: []
+ }
+ end
+
+ let(:issues_nav_items) do
+ [
+ _('List'),
+ _('Board'),
+ _('Labels'),
+ _('Milestones')
+ ]
+ end
+
+ before do
+ stub_feature_flags(sidebar_refactor: false)
+
+ visit group_path(group)
+ end
+
+ it_behaves_like 'verified navigation bar'
+ 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 45ea77e3868..551a0bc5375 100644
--- a/spec/features/groups/settings/packages_and_registries_spec.rb
+++ b/spec/features/groups/settings/packages_and_registries_spec.rb
@@ -66,28 +66,31 @@ RSpec.describe 'Group Packages & Registries settings' do
it 'automatically saves changes to the server', :js do
visit_settings_page
- expect(page).to have_content('Allow duplicates')
+ within '[data-testid="maven-settings"]' do
+ expect(page).to have_content('Allow duplicates')
- find('.gl-toggle').click
+ find('.gl-toggle').click
- expect(page).to have_content('Do not allow duplicates')
+ expect(page).to have_content('Do not allow duplicates')
- visit_settings_page
+ visit_settings_page
- expect(page).to have_content('Do not allow duplicates')
+ expect(page).to have_content('Do not allow duplicates')
+ end
end
it 'shows an error on wrong regex', :js do
visit_settings_page
- expect(page).to have_content('Allow duplicates')
-
- find('.gl-toggle').click
+ within '[data-testid="maven-settings"]' do
+ expect(page).to have_content('Allow duplicates')
- expect(page).to have_content('Do not allow duplicates')
+ find('.gl-toggle').click
- fill_in 'Exceptions', with: ')'
+ expect(page).to have_content('Do not allow duplicates')
+ fill_in 'Exceptions', with: ')'
+ end
# simulate blur event
find('body').click
@@ -98,11 +101,13 @@ RSpec.describe 'Group Packages & Registries settings' do
it 'works correctly', :js do
visit_sub_group_settings_page
- expect(page).to have_content('Allow duplicates')
+ within '[data-testid="maven-settings"]' do
+ expect(page).to have_content('Allow duplicates')
- find('.gl-toggle').click
+ find('.gl-toggle').click
- expect(page).to have_content('Do not allow duplicates')
+ expect(page).to have_content('Do not allow duplicates')
+ end
end
end
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 33d2ac50628..bcccadf7710 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'Group' do
- let_it_be(:user) { create(:user) }
+ let(:user) { create(:user) }
before do
sign_in(user)
@@ -368,21 +368,21 @@ RSpec.describe 'Group' do
expect(page).to have_content(nested_group.name)
expect(page).to have_content(project.name)
- expect(page).to have_link('Group overview')
+ expect(page).to have_link('Group information')
end
- it 'renders subgroup page with the text "Subgroup overview"' do
+ it 'renders subgroup page with the text "Subgroup information"' do
visit group_path(nested_group)
wait_for_requests
- expect(page).to have_link('Subgroup overview')
+ expect(page).to have_link('Subgroup information')
end
- it 'renders project page with the text "Project overview"' do
+ it 'renders project page with the text "Project information"' do
visit project_path(project)
wait_for_requests
- expect(page).to have_link('Project overview')
+ expect(page).to have_link('Project information')
end
end
@@ -439,6 +439,35 @@ RSpec.describe 'Group' do
end
end
+ describe 'new_repo experiment' do
+ let_it_be(:group) { create_default(:group) }
+
+ it 'when in candidate renders "project/repository"' do
+ stub_experiments(new_repo: :candidate)
+
+ visit group_path(group)
+
+ find('li.header-new.dropdown').click
+
+ page.within('li.header-new.dropdown') do
+ expect(page).to have_selector('a', text: 'New project/repository')
+ end
+ end
+
+ it 'when in control renders "project/repository"' do
+ stub_experiments(new_repo: :control)
+
+ visit group_path(group)
+
+ find('li.header-new.dropdown').click
+
+ page.within('li.header-new.dropdown') do
+ expect(page).to have_selector('a', text: 'New project')
+ expect(page).to have_no_selector('a', text: 'New project/repository')
+ end
+ end
+ end
+
def remove_with_confirm(button_text, confirm_with)
click_button button_text
fill_in 'confirm_name_input', with: confirm_with
diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb
index e9960802378..a72cf033d61 100644
--- a/spec/features/invites_spec.rb
+++ b/spec/features/invites_spec.rb
@@ -3,18 +3,16 @@
require 'spec_helper'
RSpec.describe 'Group or Project invitations', :aggregate_failures do
- let(:user) { create(:user, email: 'user@example.com') }
- let(:owner) { create(:user, name: 'John Doe') }
- let(:group) { create(:group, name: 'Owned') }
- let(:project) { create(:project, :repository, namespace: group) }
+ let_it_be(:owner) { create(:user, name: 'John Doe') }
+ let_it_be(:group) { create(:group, name: 'Owned') }
+ let_it_be(:project) { create(:project, :repository, namespace: group) }
+
let(:group_invite) { group.group_members.invite.last }
before do
stub_application_setting(require_admin_approval_after_user_signup: false)
project.add_maintainer(owner)
group.add_owner(owner)
- group.add_developer('user@example.com', owner)
- group_invite.generate_invite_token!
end
def confirm_email(new_user)
@@ -23,13 +21,13 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
visit user_confirmation_path(confirmation_token: new_user_token)
end
- def fill_in_sign_up_form(new_user)
+ def fill_in_sign_up_form(new_user, submit_button_text = 'Register')
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
fill_in 'new_user_username', with: new_user.username
fill_in 'new_user_email', with: new_user.email
fill_in 'new_user_password', with: new_user.password
- click_button 'Register'
+ click_button submit_button_text
end
def fill_in_sign_in_form(user)
@@ -44,46 +42,128 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
click_button 'Get started!'
end
- context 'when signed out' do
+ context 'when inviting a registered user' do
+ let(:invite_email) { 'user@example.com' }
+
before do
- visit invite_path(group_invite.raw_invite_token)
+ group.add_developer(invite_email, owner)
+ group_invite.generate_invite_token!
end
- it 'renders sign in page with sign in notice' do
- expect(current_path).to eq(new_user_registration_path)
- expect(page).to have_content('To accept this invitation, create an account or sign in')
- end
+ context 'when signed out' do
+ context 'when analyzing the redirects and forms from invite link click' do
+ before do
+ visit invite_path(group_invite.raw_invite_token)
+ end
- it 'pre-fills the "Username or email" field on the sign in box with the invite_email from the invite' do
- click_link 'Sign in'
+ it 'renders sign up page with sign up notice' do
+ expect(current_path).to eq(new_user_registration_path)
+ expect(page).to have_content('To accept this invitation, create an account or sign in')
+ end
- expect(find_field('Username or email').value).to eq(group_invite.invite_email)
- end
+ it 'pre-fills the "Username or email" field on the sign in box with the invite_email from the invite' do
+ click_link 'Sign in'
- it 'pre-fills the Email field on the sign up box with the invite_email from the invite' do
- expect(find_field('Email').value).to eq(group_invite.invite_email)
- end
+ expect(find_field('Username or email').value).to eq(group_invite.invite_email)
+ end
- it 'sign in, grants access and redirects to group page' do
- click_link 'Sign in'
+ it 'pre-fills the Email field on the sign up box with the invite_email from the invite' do
+ expect(find_field('Email').value).to eq(group_invite.invite_email)
+ end
+ end
- fill_in_sign_in_form(user)
+ context 'when invite is sent before account is created - ldap or social sign in for manual acceptance edge case' do
+ let(:user) { create(:user, email: 'user@example.com') }
- expect(current_path).to eq(group_path(group))
- expect(page).to have_content('You have been granted Developer access to group Owned.')
- end
- end
+ context 'when invite clicked and not signed in' do
+ before do
+ visit invite_path(group_invite.raw_invite_token)
+ end
- context 'when signed in as an existing member' do
- before do
- sign_in(owner)
- end
+ it 'sign in, grants access and redirects to group activity page' do
+ click_link 'Sign in'
- it 'shows message user already a member' do
- visit invite_path(group_invite.raw_invite_token)
+ fill_in_sign_in_form(user)
+
+ expect(current_path).to eq(activity_group_path(group))
+ end
+ end
+
+ context 'when signed in and an invite link is clicked' do
+ context 'when an invite email is a secondary email for the user' do
+ let(:invite_email) { 'user_secondary@example.com' }
+
+ before do
+ sign_in(user)
+ visit invite_path(group_invite.raw_invite_token)
+ end
+
+ it 'sends user to the invite url and allows them to decline' do
+ expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
+ expect(page).to have_content("Note that this invitation was sent to #{invite_email}")
+ expect(page).to have_content("but you are signed in as #{user.to_reference} with email #{user.email}")
+
+ click_link('Decline')
+
+ expect(page).to have_content('You have declined the invitation')
+ expect(current_path).to eq(dashboard_projects_path)
+ expect { group_invite.reload }.to raise_error ActiveRecord::RecordNotFound
+ end
+
+ it 'sends uer to the invite url and allows them to accept' do
+ expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
+ expect(page).to have_content("Note that this invitation was sent to #{invite_email}")
+ expect(page).to have_content("but you are signed in as #{user.to_reference} with email #{user.email}")
+
+ click_link('Accept invitation')
+
+ expect(page).to have_content('You have been granted')
+ expect(current_path).to eq(activity_group_path(group))
+ end
+ end
+
+ context 'when user is an existing member' do
+ before do
+ sign_in(owner)
+ visit invite_path(group_invite.raw_invite_token)
+ end
+
+ it 'shows message user already a member' do
+ expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
+ expect(page).to have_link(owner.name, href: user_url(owner))
+ expect(page).to have_content('However, you are already a member of this group.')
+ end
+ end
+ end
+
+ context 'when declining the invitation from invitation reminder email' do
+ context 'when signed in' do
+ before do
+ sign_in(user)
+ visit decline_invite_path(group_invite.raw_invite_token)
+ end
+
+ it 'declines application and redirects to dashboard' do
+ expect(current_path).to eq(dashboard_projects_path)
+ expect(page).to have_content('You have declined the invitation to join group Owned.')
+ expect { group_invite.reload }.to raise_error ActiveRecord::RecordNotFound
+ end
+ end
+
+ context 'when signed out with signup onboarding' do
+ before do
+ visit decline_invite_path(group_invite.raw_invite_token)
+ end
- expect(page).to have_link(owner.name, href: user_url(owner))
- expect(page).to have_content('However, you are already a member of this group.')
+ it 'declines application and redirects to sign in page' do
+ expect(current_path).to eq(decline_invite_path(group_invite.raw_invite_token))
+ expect(page).not_to have_content('You have declined the invitation to join')
+ expect(page).to have_content('You successfully declined the invitation')
+ expect { group_invite.reload }.to raise_error ActiveRecord::RecordNotFound
+ end
+ end
+ end
+ end
end
end
@@ -91,12 +171,15 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
let(:new_user) { build_stubbed(:user) }
let(:invite_email) { new_user.email }
let(:group_invite) { create(:group_member, :invited, group: group, invite_email: invite_email, created_by: owner) }
- let!(:project_invite) { create(:project_member, :invited, project: project, invite_email: invite_email) }
+ let(:send_email_confirmation) { true }
+
+ before do
+ stub_application_setting(send_user_confirmation_email: send_email_confirmation)
+ end
context 'when registering using invitation email' do
before do
- stub_application_setting(send_user_confirmation_email: send_email_confirmation)
- visit invite_path(group_invite.raw_invite_token)
+ visit invite_path(group_invite.raw_invite_token, invite_type: Members::InviteEmailExperiment::INVITE_TYPE)
end
context 'with admin approval required enabled' do
@@ -104,8 +187,6 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
stub_application_setting(require_admin_approval_after_user_signup: true)
end
- let(:send_email_confirmation) { true }
-
it 'does not sign the user in' do
fill_in_sign_up_form(new_user)
@@ -117,79 +198,42 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
context 'email confirmation disabled' do
let(:send_email_confirmation) { false }
- it 'signs up and redirects to the dashboard page with all the projects/groups invitations automatically accepted' do
+ it 'signs up and redirects to the most recent membership activity page with all the projects/groups invitations automatically accepted' do
fill_in_sign_up_form(new_user)
fill_in_welcome_form
- expect(current_path).to eq(dashboard_projects_path)
- expect(page).to have_content(project.full_name)
-
- visit group_path(group)
-
- expect(page).to have_content(group.full_name)
+ expect(current_path).to eq(activity_group_path(group))
+ expect(page).to have_content('You have been granted Owner access to group Owned.')
end
context 'the user sign-up using a different email address' do
let(:invite_email) { build_stubbed(:user).email }
- it 'signs up and redirects to the invitation page' do
+ it 'signs up and redirects to the activity page' do
fill_in_sign_up_form(new_user)
fill_in_welcome_form
- expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
+ expect(current_path).to eq(activity_group_path(group))
end
end
end
context 'email confirmation enabled' do
- let(:send_email_confirmation) { true }
-
- context 'when soft email confirmation is not enabled' do
- before do
- allow(User).to receive(:allow_unconfirmed_access_for).and_return 0
- end
+ context 'with members/invite_email experiment', :experiment do
+ it 'tracks the accepted invite' do
+ expect(experiment('members/invite_email')).to track(:accepted)
+ .with_context(actor: group_invite)
+ .on_next_instance
- it 'signs up and redirects to root page with all the project/groups invitation automatically accepted' do
fill_in_sign_up_form(new_user)
- confirm_email(new_user)
- fill_in_sign_in_form(new_user)
- fill_in_welcome_form
-
- expect(current_path).to eq(root_path)
- expect(page).to have_content(project.full_name)
-
- visit group_path(group)
-
- expect(page).to have_content(group.full_name)
end
end
- context 'when soft email confirmation is enabled' do
- before do
- allow(User).to receive(:allow_unconfirmed_access_for).and_return 2.days
- end
-
- it 'signs up and redirects to root page with all the project/groups invitation automatically accepted' do
- fill_in_sign_up_form(new_user)
- fill_in_welcome_form
- confirm_email(new_user)
-
- expect(current_path).to eq(root_path)
- expect(page).to have_content(project.full_name)
-
- visit group_path(group)
-
- expect(page).to have_content(group.full_name)
- end
- end
-
- it "doesn't accept invitations until the user confirms their email" do
+ it 'signs up and redirects to the group activity page with all the project/groups invitation automatically accepted' do
fill_in_sign_up_form(new_user)
fill_in_welcome_form
- sign_in(owner)
- visit project_project_members_path(project)
- expect(page).to have_content 'Invited'
+ expect(current_path).to eq(activity_group_path(group))
end
context 'the user sign-up using a different email address' do
@@ -201,13 +245,13 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
allow(User).to receive(:allow_unconfirmed_access_for).and_return 0
end
- it 'signs up and redirects to the invitation page' do
+ it 'signs up and redirects to the group activity page' do
fill_in_sign_up_form(new_user)
confirm_email(new_user)
fill_in_sign_in_form(new_user)
fill_in_welcome_form
- expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
+ expect(current_path).to eq(activity_group_path(group))
end
end
@@ -217,78 +261,75 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
allow(User).to receive(:allow_unconfirmed_access_for).and_return 2.days
end
- it 'signs up and redirects to the invitation page' do
+ it 'signs up and redirects to the group activity page' do
fill_in_sign_up_form(new_user)
fill_in_welcome_form
- expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
+ expect(current_path).to eq(activity_group_path(group))
end
end
end
end
end
- context 'when declining the invitation' do
- let(:send_email_confirmation) { true }
+ context 'with invite_signup_page_interaction experiment on', :experiment do
+ context 'with control experience' do
+ before do
+ stub_experiments(invite_signup_page_interaction: :control)
+ end
- context 'as an existing user' do
- let(:group_invite) { create(:group_member, user: user, group: group, created_by: owner) }
+ it 'lands on invite sign up page and tracks the accepted invite' do
+ expect(experiment(:invite_signup_page_interaction)).to track(:view)
+ .with_context(actor: group_invite)
+ .on_next_instance
- context 'when signed in' do
- before do
- sign_in(user)
- visit decline_invite_path(group_invite.raw_invite_token)
- end
+ visit invite_path(group_invite.raw_invite_token)
- it 'declines application and redirects to dashboard' do
- expect(current_path).to eq(dashboard_projects_path)
- expect(page).to have_content('You have declined the invitation to join group Owned.')
- expect { group_invite.reload }.to raise_error ActiveRecord::RecordNotFound
- end
- end
+ expect(current_path).to eq(new_user_registration_path)
- context 'when signed out' do
- before do
- visit decline_invite_path(group_invite.raw_invite_token)
- end
+ expect(experiment(:invite_signup_page_interaction)).to track(:form_submission)
+ .with_context(actor: group_invite)
+ .on_next_instance
- it 'declines application and redirects to sign in page' do
- expect(current_path).to eq(new_user_session_path)
- expect(page).to have_content('You have declined the invitation to join group Owned.')
- expect { group_invite.reload }.to raise_error ActiveRecord::RecordNotFound
- end
+ fill_in_sign_up_form(new_user, 'Register')
+
+ expect(current_path).to eq(users_sign_up_welcome_path)
end
end
- context 'as a non-existing user' do
+ context 'with candidate experience on .com' do
before do
- visit decline_invite_path(group_invite.raw_invite_token)
+ allow(Gitlab).to receive(:dev_env_or_com?).and_return(true)
+ stub_experiments(invite_signup_page_interaction: :candidate)
end
- it 'declines application and shows a decline page' do
- expect(current_path).to eq(decline_invite_path(group_invite.raw_invite_token))
- expect(page).to have_content('You successfully declined the invitation')
- expect { group_invite.reload }.to raise_error ActiveRecord::RecordNotFound
- end
- end
- end
+ it 'lands on invite sign up page and tracks the accepted invite' do
+ expect(experiment(:invite_signup_page_interaction)).to track(:view)
+ .with_context(actor: group_invite)
+ .on_next_instance
- context 'when accepting the invitation' do
- let(:send_email_confirmation) { true }
+ visit invite_path(group_invite.raw_invite_token)
- before do
- sign_in(user)
- visit invite_path(group_invite.raw_invite_token)
- end
+ expect(current_path).to eq(new_users_sign_up_invite_path)
+
+ expect(experiment(:invite_signup_page_interaction)).to track(:form_submission)
+ .with_context(actor: group_invite)
+ .on_next_instance
+
+ fill_in_sign_up_form(new_user, 'Continue')
- it 'grants access and redirects to group page' do
- expect(group.users.include?(user)).to be false
+ expect(current_path).to eq(users_sign_up_welcome_path)
+ end
+ end
+ end
- page.click_link 'Accept invitation'
+ context 'when declining the invitation from invitation reminder email' do
+ it 'declines application and shows a decline page' do
+ visit decline_invite_path(group_invite.raw_invite_token)
- expect(current_path).to eq(group_path(group))
- expect(page).to have_content('You have been granted Owner access to group Owned.')
- expect(group.users.include?(user)).to be true
+ expect(current_path).to eq(decline_invite_path(group_invite.raw_invite_token))
+ expect(page).to have_content('You successfully declined the invitation')
+ expect { group_invite.reload }.to raise_error ActiveRecord::RecordNotFound
end
end
end
diff --git a/spec/features/issuables/sorting_list_spec.rb b/spec/features/issuables/sorting_list_spec.rb
index d065e96885c..6e07c6ffed2 100644
--- a/spec/features/issuables/sorting_list_spec.rb
+++ b/spec/features/issuables/sorting_list_spec.rb
@@ -57,7 +57,7 @@ RSpec.describe 'Sort Issuable List' do
it 'is "last updated"' do
visit_merge_requests_with_state(project, 'merged')
- expect(find('.filter-dropdown-container')).to have_content('Last updated')
+ expect(page).to have_button 'Last updated'
expect(first_merge_request).to include(last_updated_issuable.title)
expect(last_merge_request).to include(first_updated_issuable.title)
end
@@ -69,7 +69,7 @@ RSpec.describe 'Sort Issuable List' do
it 'is "last updated"' do
visit_merge_requests_with_state(project, 'closed')
- expect(find('.filter-dropdown-container')).to have_content('Last updated')
+ expect(page).to have_button 'Last updated'
expect(first_merge_request).to include(last_updated_issuable.title)
expect(last_merge_request).to include(first_updated_issuable.title)
end
@@ -81,7 +81,7 @@ RSpec.describe 'Sort Issuable List' do
it 'is "created date"' do
visit_merge_requests_with_state(project, 'all')
- expect(find('.filter-dropdown-container')).to have_content('Created date')
+ expect(page).to have_button 'Created date'
expect(first_merge_request).to include(last_created_issuable.title)
expect(last_merge_request).to include(first_created_issuable.title)
end
@@ -94,15 +94,13 @@ RSpec.describe 'Sort Issuable List' do
it 'supports sorting in asc and desc order' do
visit_merge_requests_with_state(project, 'open')
- page.within('.filter-dropdown-container') do
- click_button('Created date')
- click_link('Last updated')
- end
+ click_button('Created date')
+ click_link('Last updated')
expect(first_merge_request).to include(last_updated_issuable.title)
expect(last_merge_request).to include(first_updated_issuable.title)
- find('.filter-dropdown-container .rspec-reverse-sort').click
+ click_on 'Sort direction'
expect(first_merge_request).to include(first_updated_issuable.title)
expect(last_merge_request).to include(last_updated_issuable.title)
@@ -133,7 +131,7 @@ RSpec.describe 'Sort Issuable List' do
it 'is "created date"' do
visit_issues project
- expect(find('.filter-dropdown-container')).to have_content('Created date')
+ expect(page).to have_button 'Created date'
expect(first_issue).to include(last_created_issuable.title)
expect(last_issue).to include(first_created_issuable.title)
end
@@ -145,7 +143,7 @@ RSpec.describe 'Sort Issuable List' do
it 'is "created date"' do
visit_issues_with_state(project, 'opened')
- expect(find('.filter-dropdown-container')).to have_content('Created date')
+ expect(page).to have_button 'Created date'
expect(first_issue).to include(last_created_issuable.title)
expect(last_issue).to include(first_created_issuable.title)
end
@@ -157,7 +155,7 @@ RSpec.describe 'Sort Issuable List' do
it 'is "last updated"' do
visit_issues_with_state(project, 'closed')
- expect(find('.filter-dropdown-container')).to have_content('Last updated')
+ expect(page).to have_button 'Last updated'
expect(first_issue).to include(last_updated_issuable.title)
expect(last_issue).to include(first_updated_issuable.title)
end
@@ -169,7 +167,7 @@ RSpec.describe 'Sort Issuable List' do
it 'is "created date"' do
visit_issues_with_state(project, 'all')
- expect(find('.filter-dropdown-container')).to have_content('Created date')
+ expect(page).to have_button 'Created date'
expect(first_issue).to include(last_created_issuable.title)
expect(last_issue).to include(first_created_issuable.title)
end
@@ -183,7 +181,7 @@ RSpec.describe 'Sort Issuable List' do
end
it 'shows the sort order as created date' do
- expect(find('.filter-dropdown-container')).to have_content('Created date')
+ expect(page).to have_button 'Created date'
expect(first_issue).to include(last_created_issuable.title)
expect(last_issue).to include(first_created_issuable.title)
end
@@ -196,15 +194,17 @@ RSpec.describe 'Sort Issuable List' do
it 'supports sorting in asc and desc order' do
visit_issues_with_state(project, 'opened')
- page.within('.filter-dropdown-container') do
- click_button('Created date')
- click_link('Last updated')
- end
+ click_button('Created date')
+ click_on('Last updated')
+
+ wait_for_requests
expect(first_issue).to include(last_updated_issuable.title)
expect(last_issue).to include(first_updated_issuable.title)
- find('.filter-dropdown-container .rspec-reverse-sort').click
+ click_on 'Sort direction'
+
+ wait_for_requests
expect(first_issue).to include(first_updated_issuable.title)
expect(last_issue).to include(last_updated_issuable.title)
diff --git a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
index 34d78880991..a4c0a84af7d 100644
--- a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
+++ b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
@@ -72,7 +72,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
end
it 'shows a warning that the merge request contains unresolved threads' do
- expect(page).to have_content 'Before this can be merged,'
+ expect(page).to have_content 'all threads must be resolved'
end
it 'has a link to resolve all threads by creating an issue' do
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index 4f4584e7dce..88a7b890daa 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -79,26 +79,6 @@ RSpec.describe 'Filter issues', :js do
expect_filtered_search_input(search_term)
end
- context 'with the NOT queries feature flag disabled' do
- before do
- stub_feature_flags(not_issuable_queries: false)
- visit project_issues_path(project)
- end
-
- it 'does not have the != option' do
- input_filtered_search("label:", submit: false, extra_space: false)
-
- wait_for_requests
- within('#js-dropdown-operator') do
- tokens = all(:css, 'li.filter-dropdown-item')
- expect(tokens.count).to eq(1)
- button = tokens[0].find('button')
- expect(button).to have_content('=')
- expect(button).not_to have_content('!=')
- end
- end
- end
-
describe 'filter issues by author' do
context 'only author' do
it 'filters issues by searched author' do
@@ -350,7 +330,7 @@ RSpec.describe 'Filter issues', :js do
context 'issue label clicked' do
it 'filters and displays in search bar' do
- find('[data-qa-selector="issuable-label"]', text: multiple_words_label.title).click
+ click_link multiple_words_label.title
expect_issues_list_count(1)
expect_tokens([label_token("\"#{multiple_words_label.title}\"")])
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 04b4caa52fe..d147476f1ab 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -5,17 +5,14 @@ require 'spec_helper'
RSpec.describe 'Issue Sidebar' do
include MobileHelpers
- let(:group) { create(:group, :nested) }
- let(:project) { create(:project, :public, namespace: group) }
- let!(:user) { create(:user) }
- 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) }
+ let_it_be(:group) { create(:group, :nested) }
+ let_it_be(:project) { create(:project, :public, namespace: group) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:label) { create(:label, project: project, title: 'bug') }
+ let_it_be(:issue) { create(:labeled_issue, project: project, labels: [label]) }
+ let_it_be(:mock_date) { Date.today.at_beginning_of_month + 2.days }
+ let_it_be(:issue_with_due_date) { create(:issue, project: project, due_date: mock_date) }
+ let_it_be(:xss_label) { create(:label, project: project, title: '&lt;script&gt;alert("xss");&lt;&#x2F;script&gt;') }
before do
stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
@@ -130,30 +127,7 @@ RSpec.describe 'Issue Sidebar' do
end
end
- context 'when invite_members_version_b experiment is enabled' do
- before do
- stub_experiment_for_subject(invite_members_version_b: true)
- end
-
- it 'shows a link for inviting members and follows through to modal' do
- project.add_developer(user)
- visit_issue(project, issue2)
-
- open_assignees_dropdown
-
- page.within '.dropdown-menu-user' do
- expect(page).to have_link('Invite members', href: '#')
- expect(page).to have_selector('[data-track-event="click_invite_members_version_b"]')
- expect(page).to have_selector('[data-track-label="edit_assignee"]')
- end
-
- click_link 'Invite members'
-
- expect(page).to have_content("Oops, this feature isn't ready yet")
- end
- end
-
- context 'when invite_members_version_b experiment is disabled' do
+ context 'when user cannot invite members in assignee dropdown' do
it 'shows author in assignee dropdown and no invite link' do
project.add_developer(user)
visit_issue(project, issue2)
@@ -212,7 +186,8 @@ RSpec.describe 'Issue Sidebar' do
click_link user2.name
end
- find('.js-right-sidebar').click
+ find('.participants').click
+ wait_for_requests
open_assignees_dropdown
@@ -226,7 +201,31 @@ RSpec.describe 'Issue Sidebar' do
end
end
- context 'as a allowed user' do
+ context 'due date widget', :js do
+ let(:due_date_value) { find('[data-testid="due-date"] [data-testid="sidebar-date-value"]') }
+
+ context 'when no due date exists' do
+ before do
+ visit_issue(project, issue)
+ end
+
+ it "displays 'None'" do
+ expect(due_date_value.text).to have_content 'None'
+ end
+ end
+
+ context 'when due date exists' do
+ before do
+ visit_issue(project, issue_with_due_date)
+ end
+
+ it "displays the due date" do
+ expect(due_date_value.text).to have_content mock_date.strftime('%b %-d, %Y')
+ end
+ end
+ end
+
+ context 'as an allowed user' do
before do
project.add_developer(user)
visit_issue(project, issue)
@@ -260,6 +259,12 @@ RSpec.describe 'Issue Sidebar' do
end
context 'editing issue milestone', :js do
+ let_it_be(:milestone_expired) { create(:milestone, project: project, due_date: 5.days.ago) }
+ let_it_be(:milestone_no_duedate) { create(:milestone, project: project, title: 'Foo - No due date') }
+ let_it_be(:milestone1) { create(:milestone, project: project, title: 'Milestone-1', due_date: 20.days.from_now) }
+ let_it_be(:milestone2) { create(:milestone, project: project, title: 'Milestone-2', due_date: 15.days.from_now) }
+ let_it_be(:milestone3) { create(:milestone, project: project, title: 'Milestone-3', due_date: 10.days.from_now) }
+
before do
page.within('.block.milestone > .title') do
click_on 'Edit'
@@ -448,6 +453,8 @@ RSpec.describe 'Issue Sidebar' do
def visit_issue(project, issue)
visit project_issue_path(project, issue)
+
+ wait_for_requests
end
def open_issue_sidebar
diff --git a/spec/features/issues/resource_label_events_spec.rb b/spec/features/issues/resource_label_events_spec.rb
index 8faec85f3df..33edf2f0b63 100644
--- a/spec/features/issues/resource_label_events_spec.rb
+++ b/spec/features/issues/resource_label_events_spec.rb
@@ -40,7 +40,7 @@ RSpec.describe 'List issue resource label events', :js do
labels.each { |label| click_link label }
- click_on 'Edit'
+ send_keys(:escape)
wait_for_requests
end
end
diff --git a/spec/features/issues/service_desk_spec.rb b/spec/features/issues/service_desk_spec.rb
index 75ea8c14f7f..0a879fdd4d4 100644
--- a/spec/features/issues/service_desk_spec.rb
+++ b/spec/features/issues/service_desk_spec.rb
@@ -9,6 +9,8 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
let_it_be(:support_bot) { User.support_bot }
before do
+ stub_feature_flags(vue_issuables_list: true)
+
# The following two conditions equate to Gitlab::ServiceDesk.supported == true
allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true)
allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?).and_return(true)
@@ -21,7 +23,7 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
before do
visit project_path(project)
find('.sidebar-top-level-items .shortcuts-issues').click
- find('.sidebar-sub-level-items a[title="Service Desk"]').click
+ find('.sidebar-sub-level-items a', text: 'Service Desk').click
end
it 'can navigate to the service desk from link in the sidebar' do
diff --git a/spec/features/issues/spam_issues_spec.rb b/spec/features/issues/spam_issues_spec.rb
index 461030d3176..70d7deadec3 100644
--- a/spec/features/issues/spam_issues_spec.rb
+++ b/spec/features/issues/spam_issues_spec.rb
@@ -14,6 +14,7 @@ RSpec.describe 'New issue', :js do
Gitlab::CurrentSettings.update!(
akismet_enabled: true,
akismet_api_key: 'testkey',
+ spam_check_api_key: 'testkey',
recaptcha_enabled: true,
recaptcha_site_key: 'test site key',
recaptcha_private_key: 'test private key'
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/user_bulk_edits_issues_labels_spec.rb
index 80bf964e2ee..97df2d0208b 100644
--- a/spec/features/issues/bulk_assignment_labels_spec.rb
+++ b/spec/features/issues/user_bulk_edits_issues_labels_spec.rb
@@ -20,17 +20,13 @@ RSpec.describe 'Issues > Labels bulk assignment' do
end
context 'sidebar' do
- before do
- enable_bulk_update
- end
-
it 'is present when bulk edit is enabled' do
- expect(page).to have_css('.issuable-sidebar')
+ enable_bulk_update
+ expect(page).to have_css 'aside[aria-label="Bulk update"]'
end
it 'is not present when bulk edit is disabled' do
- disable_bulk_update
- expect(page).not_to have_css('.issuable-sidebar')
+ expect(page).not_to have_css 'aside[aria-label="Bulk update"]'
end
end
@@ -42,7 +38,7 @@ RSpec.describe 'Issues > Labels bulk assignment' do
context 'a label' do
context 'to all issues' do
before do
- check 'check-all-issues'
+ check 'Select all'
open_labels_dropdown ['bug']
update_issues
end
@@ -57,8 +53,8 @@ RSpec.describe 'Issues > Labels bulk assignment' do
context 'to some issues' do
before do
- check "selected_issue_#{issue1.id}"
- check "selected_issue_#{issue2.id}"
+ check issue1.title
+ check issue2.title
open_labels_dropdown ['bug']
update_issues
end
@@ -73,7 +69,7 @@ RSpec.describe 'Issues > Labels bulk assignment' do
context 'to an issue' do
before do
- check "selected_issue_#{issue1.id}"
+ check issue1.title
open_labels_dropdown ['bug']
update_issues
end
@@ -89,7 +85,7 @@ RSpec.describe 'Issues > Labels bulk assignment' do
context 'to an issue by selecting the label first' do
before do
open_labels_dropdown ['bug']
- check "selected_issue_#{issue1.id}"
+ check issue1.title
update_issues
end
@@ -105,7 +101,7 @@ RSpec.describe 'Issues > Labels bulk assignment' do
context 'multiple labels' do
context 'to all issues' do
before do
- check 'check-all-issues'
+ check 'Select all'
open_labels_dropdown %w(bug feature)
update_issues
end
@@ -120,7 +116,7 @@ RSpec.describe 'Issues > Labels bulk assignment' do
context 'to a issue' do
before do
- check "selected_issue_#{issue1.id}"
+ check issue1.title
open_labels_dropdown %w(bug feature)
update_issues
end
@@ -141,7 +137,7 @@ RSpec.describe 'Issues > Labels bulk assignment' do
issue2.labels << feature
enable_bulk_update
- check 'check-all-issues'
+ check 'Select all'
open_labels_dropdown ['bug']
update_issues
@@ -162,7 +158,7 @@ RSpec.describe 'Issues > Labels bulk assignment' do
issue2.labels << feature
enable_bulk_update
- check 'check-all-issues'
+ check 'Select all'
unmark_labels_in_dropdown %w(bug feature)
update_issues
end
@@ -229,7 +225,7 @@ RSpec.describe 'Issues > Labels bulk assignment' do
expect(find("#issue_#{issue1.id}")).to have_content 'bug'
expect(find("#issue_#{issue2.id}")).to have_content 'feature'
- check 'check-all-issues'
+ check 'Select all'
open_milestone_dropdown(['First Release'])
update_issues
@@ -250,7 +246,7 @@ RSpec.describe 'Issues > Labels bulk assignment' do
it 'keeps existing label and new label is present' do
expect(find("#issue_#{issue1.id}")).to have_content 'bug'
- check 'check-all-issues'
+ check 'Select all'
open_milestone_dropdown ['First Release']
open_labels_dropdown ['feature']
update_issues
@@ -277,7 +273,7 @@ RSpec.describe 'Issues > Labels bulk assignment' do
expect(find("#issue_#{issue1.id}")).to have_content 'bug'
expect(find("#issue_#{issue2.id}")).to have_content 'feature'
- check 'check-all-issues'
+ check 'Select all'
open_milestone_dropdown ['First Release']
unmark_labels_in_dropdown ['feature']
@@ -309,7 +305,7 @@ RSpec.describe 'Issues > Labels bulk assignment' do
expect(find("#issue_#{issue2.id}")).to have_content 'feature'
expect(find("#issue_#{issue2.id}")).to have_content 'First Release'
- check 'check-all-issues'
+ check 'Select all'
open_milestone_dropdown(['No milestone'])
update_issues
@@ -369,31 +365,31 @@ RSpec.describe 'Issues > Labels bulk assignment' do
end
it 'applies label from filtered results' do
- check 'check-all-issues'
+ check 'Select all'
- page.within('.issues-bulk-update') do
+ within('aside[aria-label="Bulk update"]') do
click_button 'Select labels'
wait_for_requests
- expect(find('.dropdown-menu-labels li', text: 'bug')).to have_css('.is-active')
- expect(find('.dropdown-menu-labels li', text: 'feature')).to have_css('.is-indeterminate')
+ expect(page).to have_link 'bug', class: 'is-active'
+ expect(page).to have_link 'feature', class: 'is-indeterminate'
click_link 'bug'
- find('.dropdown-input-field', visible: true).set('wontfix')
+ fill_in 'Search', with: 'wontfix'
click_link 'wontfix'
end
update_issues
- page.within '.issues-holder' do
- expect(find("#issue_#{issue1.id}")).not_to have_content 'bug'
- expect(find("#issue_#{issue1.id}")).to have_content 'feature'
- expect(find("#issue_#{issue1.id}")).to have_content 'wontfix'
+ first_issue = find("#issue_#{issue1.id}")
+ expect(first_issue).not_to have_content 'bug'
+ expect(first_issue).to have_content 'feature'
+ expect(first_issue).to have_content 'wontfix'
- expect(find("#issue_#{issue2.id}")).not_to have_content 'bug'
- expect(find("#issue_#{issue2.id}")).not_to have_content 'feature'
- expect(find("#issue_#{issue2.id}")).to have_content 'wontfix'
- end
+ second_issue = find("#issue_#{issue2.id}")
+ expect(second_issue).not_to have_content 'bug'
+ expect(second_issue).not_to have_content 'feature'
+ expect(second_issue).to have_content 'wontfix'
end
end
end
@@ -408,24 +404,22 @@ RSpec.describe 'Issues > Labels bulk assignment' do
context 'cannot bulk assign labels' do
it do
expect(page).not_to have_button 'Edit issues'
- expect(page).not_to have_css '.check-all-issues'
- expect(page).not_to have_css '.issue-check'
+ expect(page).not_to have_unchecked_field 'Select all'
+ expect(page).not_to have_unchecked_field issue1.title
end
end
end
def open_milestone_dropdown(items = [])
- page.within('.issues-bulk-update') do
- click_button 'Select milestone'
- wait_for_requests
- items.map do |item|
- click_link item
- end
+ click_button 'Select milestone'
+ wait_for_requests
+ items.map do |item|
+ click_link item
end
end
def open_labels_dropdown(items = [], unmark = false)
- page.within('.issues-bulk-update') do
+ within('aside[aria-label="Bulk update"]') do
click_button 'Select labels'
wait_for_requests
items.map do |item|
@@ -446,12 +440,10 @@ RSpec.describe 'Issues > Labels bulk assignment' do
end
def check_issue(issue, uncheck = false)
- page.within('.issues-list') do
- if uncheck
- uncheck "selected_issue_#{issue.id}"
- else
- check "selected_issue_#{issue.id}"
- end
+ if uncheck
+ uncheck issue.title
+ else
+ check issue.title
end
end
@@ -460,12 +452,13 @@ RSpec.describe 'Issues > Labels bulk assignment' do
end
def update_issues
- find('.update-selected-issues').click
+ click_button 'Update all'
wait_for_requests
end
def enable_bulk_update
visit project_issues_path(project)
+ wait_for_requests
click_button 'Edit issues'
end
diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/user_bulk_edits_issues_spec.rb
index eb78e4e2456..e34c16e27ba 100644
--- a/spec/features/issues/update_issues_spec.rb
+++ b/spec/features/issues/user_bulk_edits_issues_spec.rb
@@ -17,10 +17,10 @@ RSpec.describe 'Multiple issue updating from issues#index', :js do
visit project_issues_path(project)
click_button 'Edit issues'
- find('#check-all-issues').click
- find('.js-issue-status').click
+ check 'Select all'
+ click_button 'Select status'
+ click_link 'Closed'
- find('.dropdown-menu-status a', text: 'Closed').click
click_update_issues_button
expect(page).to have_selector('.issue', count: 0)
end
@@ -30,10 +30,10 @@ RSpec.describe 'Multiple issue updating from issues#index', :js do
visit project_issues_path(project, state: 'closed')
click_button 'Edit issues'
- find('#check-all-issues').click
- find('.js-issue-status').click
+ check 'Select all'
+ click_button 'Select status'
+ click_link 'Open'
- find('.dropdown-menu-status a', text: 'Open').click
click_update_issues_button
expect(page).to have_selector('.issue', count: 0)
end
@@ -44,10 +44,10 @@ RSpec.describe 'Multiple issue updating from issues#index', :js do
visit project_issues_path(project)
click_button 'Edit issues'
- find('#check-all-issues').click
+ check 'Select all'
click_update_assignee_button
+ click_link user.username
- find('.dropdown-menu-user-link', text: user.username).click
click_update_issues_button
page.within('.issue .controls') do
@@ -59,13 +59,15 @@ RSpec.describe 'Multiple issue updating from issues#index', :js do
create_assigned
visit project_issues_path(project)
+ expect(find('.issue:first-of-type')).to have_link "Assigned to #{user.name}"
+
click_button 'Edit issues'
- find('#check-all-issues').click
+ check 'Select all'
click_update_assignee_button
-
click_link 'Unassigned'
click_update_issues_button
- expect(find('.issue:first-child .controls')).not_to have_css('.author-link')
+
+ expect(find('.issue:first-of-type')).not_to have_link "Assigned to #{user.name}"
end
end
@@ -76,10 +78,9 @@ RSpec.describe 'Multiple issue updating from issues#index', :js do
visit project_issues_path(project)
click_button 'Edit issues'
- find('#check-all-issues').click
- find('.issues-bulk-update .js-milestone-select').click
-
- find('.dropdown-menu-milestone a', text: milestone.title).click
+ check 'Select all'
+ click_button 'Select milestone'
+ click_link milestone.title
click_update_issues_button
expect(page.find('.issue')).to have_content milestone.title
@@ -91,16 +92,15 @@ RSpec.describe 'Multiple issue updating from issues#index', :js do
wait_for_requests
- expect(first('.issue')).to have_content milestone.title
+ expect(find('.issue:first-of-type')).to have_text milestone.title
click_button 'Edit issues'
- find('#check-all-issues').click
- find('.issues-bulk-update .js-milestone-select').click
-
- find('.dropdown-menu-milestone a', text: "No milestone").click
+ check 'Select all'
+ click_button 'Select milestone'
+ click_link 'No milestone'
click_update_issues_button
- expect(find('.issue:first-child')).not_to have_content milestone.title
+ expect(find('.issue:first-of-type')).not_to have_text milestone.title
end
end
@@ -117,12 +117,12 @@ RSpec.describe 'Multiple issue updating from issues#index', :js do
end
def click_update_assignee_button
- find('.js-update-assignee').click
+ click_button 'Select assignee'
wait_for_requests
end
def click_update_issues_button
- find('.update-selected-issues').click
+ click_button 'Update all'
wait_for_requests
end
end
diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb
index 1bbb96ff479..cb4a5a32762 100644
--- a/spec/features/issues/user_edits_issue_spec.rb
+++ b/spec/features/issues/user_edits_issue_spec.rb
@@ -406,6 +406,12 @@ RSpec.describe "Issues > User edits issue", :js do
end
context 'update due date' do
+ before do
+ # Due date widget uses GraphQL and needs to wait for requests to come back
+ # The date picker won't be rendered before requests complete
+ wait_for_requests
+ end
+
it 'adds due date to issue' do
date = Date.today.at_beginning_of_month + 2.days
@@ -417,7 +423,7 @@ RSpec.describe "Issues > User edits issue", :js do
wait_for_requests
- expect(find('[data-testid="sidebar-duedate-value"]').text).to have_content date.strftime('%b %-d, %Y')
+ expect(find('[data-testid="sidebar-date-value"]').text).to have_content date.strftime('%b %-d, %Y')
end
end
diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb
index e862f7030c0..bbb7e8a028d 100644
--- a/spec/features/issues/user_interacts_with_awards_spec.rb
+++ b/spec/features/issues/user_interacts_with_awards_spec.rb
@@ -5,10 +5,6 @@ require 'spec_helper'
RSpec.describe 'User interacts with awards' do
let(:user) { create(:user) }
- before do
- stub_feature_flags(improved_emoji_picker: false)
- end
-
describe 'User interacts with awards in an issue', :js do
let(:issue) { create(:issue, project: project)}
let(:project) { create(:project) }
@@ -55,29 +51,24 @@ RSpec.describe 'User interacts with awards' do
it 'toggles a custom award emoji' do
page.within('.awards') do
- page.find('.js-add-award').click
+ page.find('.add-reaction-button').click
end
- page.find('.emoji-menu.is-visible')
-
- expect(page).to have_selector('.js-emoji-menu-search')
- expect(page.evaluate_script("document.activeElement.classList.contains('js-emoji-menu-search')")).to eq(true)
-
- page.within('.emoji-menu-content') do
- emoji_button = page.first('.js-emoji-btn')
+ page.within('.emoji-picker') do
+ emoji_button = page.first('gl-emoji[data-name="8ball"]')
emoji_button.hover
emoji_button.click
end
page.within('.awards') do
- expect(page).to have_selector('.js-emoji-btn')
- expect(page.find('.js-emoji-btn.active .js-counter')).to have_content('1')
- expect(page).to have_css(".js-emoji-btn.active[title='You']")
+ expect(page).to have_selector('[data-testid="award-button"]')
+ expect(page.find('[data-testid="award-button"].is-active .js-counter')).to have_content('1')
+ expect(page).to have_css('[data-testid="award-button"].is-active[title="You"]')
expect do
- page.find('.js-emoji-btn.active').click
+ page.find('[data-testid="award-button"].is-active').click
wait_for_requests
- end.to change { page.all('.award-control.js-emoji-btn').size }.from(3).to(2)
+ end.to change { page.all('[data-testid="award-button"]').size }.from(3).to(2)
end
end
@@ -212,31 +203,25 @@ RSpec.describe 'User interacts with awards' do
end
it 'adds award to issue' do
- first('.js-emoji-btn').click
+ first('[data-testid="award-button"]').click
- expect(page).to have_selector('.js-emoji-btn.active')
- expect(first('.js-emoji-btn')).to have_content '1'
+ expect(page).to have_selector('[data-testid="award-button"].is-active')
+ expect(first('[data-testid="award-button"]')).to have_content '1'
visit project_issue_path(project, issue)
- expect(first('.js-emoji-btn')).to have_content '1'
+ expect(first('[data-testid="award-button"]')).to have_content '1'
end
it 'removes award from issue' do
- first('.js-emoji-btn').click
- find('.js-emoji-btn.active').click
+ first('[data-testid="award-button"]').click
+ find('[data-testid="award-button"].is-active').click
- expect(first('.js-emoji-btn')).to have_content '0'
+ expect(first('[data-testid="award-button"]')).to have_content '0'
visit project_issue_path(project, issue)
- expect(first('.js-emoji-btn')).to have_content '0'
- end
-
- it 'only has one menu on the page' do
- first('.js-add-award').click
-
- expect(page).to have_selector('.emoji-menu', count: 1)
+ expect(first('[data-testid="award-button"]')).to have_content '0'
end
end
@@ -311,7 +296,7 @@ RSpec.describe 'User interacts with awards' do
end
context 'execute /award quick action' do
- it 'toggles the emoji award on noteable', :js do
+ xit 'toggles the emoji award on noteable', :js do
execute_quick_action('/award :100:')
expect(find(noteable_award_counter)).to have_text("1")
@@ -330,7 +315,7 @@ RSpec.describe 'User interacts with awards' do
end
it 'has disabled emoji button' do
- expect(first('.award-control')[:class]).to have_text('disabled')
+ expect(first('[data-testid="award-button"]')[:class]).to have_text('disabled')
end
end
@@ -356,7 +341,7 @@ RSpec.describe 'User interacts with awards' do
end
def noteable_award_counter
- ".awards .active"
+ ".awards .is-active"
end
def toggle_smiley_emoji(status)
diff --git a/spec/features/issues/user_toggles_subscription_spec.rb b/spec/features/issues/user_toggles_subscription_spec.rb
index d91c187c840..35f4b415463 100644
--- a/spec/features/issues/user_toggles_subscription_spec.rb
+++ b/spec/features/issues/user_toggles_subscription_spec.rb
@@ -32,8 +32,8 @@ RSpec.describe "User toggles subscription", :js do
let(:project) { create(:project_empty_repo, :public, emails_disabled: true) }
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('[data-testid="subscription-toggle"]')
+ expect(page).to have_content('Disabled by project owner')
+ expect(page).to have_button('Notifications', class: 'is-disabled')
end
end
end
diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb
index c9dc764f93b..c700f878df6 100644
--- a/spec/features/markdown/copy_as_gfm_spec.rb
+++ b/spec/features/markdown/copy_as_gfm_spec.rb
@@ -426,8 +426,8 @@ RSpec.describe 'Copy as GFM', :js do
html = <<~HTML
<div class="md-suggestion">
- <div class="md-suggestion-header border-bottom-0 mt-2 qa-suggestion-diff-header js-suggestion-diff-header">
- <div class="qa-suggestion-diff-header js-suggestion-diff-header font-weight-bold">
+ <div class="md-suggestion-header border-bottom-0 mt-2 js-suggestion-diff-header">
+ <div class="js-suggestion-diff-header font-weight-bold">
Suggested change
<a href="/gitlab/help/user/discussions/index.md#suggest-changes" aria-label="Help" class="js-help-btn">
<svg aria-hidden="true" class="s16 ic-question-o link-highlight">
diff --git a/spec/features/markdown/math_spec.rb b/spec/features/markdown/math_spec.rb
index 441cff7045f..fa23fac2f96 100644
--- a/spec/features/markdown/math_spec.rb
+++ b/spec/features/markdown/math_spec.rb
@@ -13,14 +13,24 @@ RSpec.describe 'Math rendering', :js do
```math
a^2+b^2=c^2
```
+
+ This math is aligned
+
+ ```math
+ \\begin{align*}
+ a&=b+c \\\\
+ d+e&=f
+ \\end{align*}
+ ```
MATH
issue = create(:issue, project: project, description: description)
visit project_issue_path(project, issue)
- expect(page).to have_selector('.katex .mord.mathdefault', text: 'b')
- expect(page).to have_selector('.katex-display .mord.mathdefault', text: 'b')
+ expect(page).to have_selector('.katex .mord.mathnormal', text: 'b')
+ expect(page).to have_selector('.katex-display .mord.mathnormal', text: 'b')
+ expect(page).to have_selector('.katex-display .mtable .col-align-l .mord.mathnormal', text: 'f')
end
it 'only renders non XSS links' do
@@ -35,7 +45,9 @@ RSpec.describe 'Math rendering', :js do
visit project_issue_path(project, issue)
page.within '.description > .md' do
- expect(page).to have_selector('.katex-error')
+ # unfortunately there is no class selector for KaTeX's "unsupported command"
+ # formatting so we must match the style attribute
+ expect(page).to have_selector('.katex-html .mord[style*="color:"][style*="#cc0000"]', text: '\href')
expect(page).to have_selector('.katex-html a', text: 'Gitlab')
end
end
diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb
index 19680a827bf..5b11d9cb919 100644
--- a/spec/features/merge_request/batch_comments_spec.rb
+++ b/spec/features/merge_request/batch_comments_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
expect(page).to have_css('.review-bar-component')
- expect(find('.review-bar-content .btn-success')).to have_content('1')
+ expect(find('.review-bar-content .btn-confirm')).to have_content('1')
end
it 'publishes review' do
@@ -157,7 +157,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
expect(find('.new .draft-note-component')).to have_content('Line is wrong')
expect(find('.old .draft-note-component')).to have_content('Another wrong line')
- expect(find('.review-bar-content .btn-success')).to have_content('2')
+ expect(find('.review-bar-content .btn-confirm')).to have_content('2')
end
end
diff --git a/spec/features/merge_request/user_creates_merge_request_spec.rb b/spec/features/merge_request/user_creates_merge_request_spec.rb
index 119cf31098c..617aceae54c 100644
--- a/spec/features/merge_request/user_creates_merge_request_spec.rb
+++ b/spec/features/merge_request/user_creates_merge_request_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe "User creates a merge request", :js do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
+
let(:title) { "Some feature" }
before do
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 63b463a2c5f..9e1b0135932 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
@@ -64,7 +64,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do
context 'when enabled after it was previously canceled' do
before do
click_button "Merge when pipeline succeeds"
- click_link "Cancel automatic merge"
+ click_link "Cancel"
wait_for_requests
@@ -87,7 +87,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do
before do
merge_request.merge_params['force_remove_source_branch'] = '0'
merge_request.save!
- click_link "Cancel automatic merge"
+ click_link "Cancel"
end
it_behaves_like 'Merge when pipeline succeeds activator'
@@ -114,7 +114,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do
end
it 'allows to cancel the automatic merge' do
- click_link "Cancel automatic merge"
+ click_link "Cancel"
expect(page).to have_button "Merge when pipeline succeeds"
diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb
index a6dfae72912..83d9388914b 100644
--- a/spec/features/merge_request/user_posts_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_notes_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe 'Merge request > User posts notes', :js do
include NoteInteractionHelpers
let_it_be(:project) { create(:project, :repository) }
+
let(:user) { project.creator }
let(:merge_request) do
create(:merge_request, source_project: project, target_project: project)
diff --git a/spec/features/merge_request/user_resolves_conflicts_spec.rb b/spec/features/merge_request/user_resolves_conflicts_spec.rb
index 1b1152897fc..d9e3bfd6a9c 100644
--- a/spec/features/merge_request/user_resolves_conflicts_spec.rb
+++ b/spec/features/merge_request/user_resolves_conflicts_spec.rb
@@ -173,7 +173,7 @@ RSpec.describe 'Merge request > User resolves conflicts', :js do
end
it "renders bad name without xss issues" do
- expect(find('.resolve-conflicts-form .resolve-info')).to have_content(bad_branch_name)
+ expect(find('[data-testid="resolve-info"]')).to have_content(bad_branch_name)
end
end
end
diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
index 9a3f97a0943..73e628bda98 100644
--- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
+++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
@@ -131,7 +131,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
expect(page).not_to have_selector('.diffs .diff-file .notes_holder')
end
- it 'shows resolved thread when toggled' do
+ it 'shows resolved thread when toggled', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/327439' do
find('.diff-comment-avatar-holders').click
expect(find('.diffs .diff-file .notes_holder')).to be_visible
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 78c1b2a718e..35be21a646e 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
@@ -15,7 +15,7 @@ RSpec.describe 'Merge request > User cherry-picks', :js do
context 'Viewing a merged merge request' do
before do
- service = MergeRequests::MergeService.new(project, user, sha: merge_request.diff_head_sha)
+ service = MergeRequests::MergeService.new(project: project, current_user: user, params: { sha: merge_request.diff_head_sha })
perform_enqueued_jobs do
service.execute(merge_request)
diff --git a/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb b/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb
index ac38b2b854c..e250837f398 100644
--- a/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe 'Merge request > User sees merge button depending on unresolved t
context 'with unresolved threads' do
it 'does not allow to merge' do
expect(page).not_to have_button 'Merge'
- expect(page).to have_content('Before this can be merged,')
+ expect(page).to have_content('all threads must be resolved')
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 733b5a97fea..85eb956033b 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
@@ -147,7 +147,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
context 'when detached merge request pipeline is pending' do
it 'waits the head pipeline' do
expect(page).to have_content('to be merged automatically when the pipeline succeeds')
- expect(page).to have_link('Cancel automatic merge')
+ expect(page).to have_link('Cancel')
end
end
@@ -174,7 +174,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'waits the head pipeline' do
expect(page).to have_content('to be merged automatically when the pipeline succeeds')
- expect(page).to have_link('Cancel automatic merge')
+ expect(page).to have_link('Cancel')
end
end
end
@@ -369,7 +369,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
context 'when detached merge request pipeline is pending' do
it 'waits the head pipeline' do
expect(page).to have_content('to be merged automatically when the pipeline succeeds')
- expect(page).to have_link('Cancel automatic merge')
+ expect(page).to have_link('Cancel')
end
end
@@ -395,7 +395,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'waits the head pipeline' do
expect(page).to have_content('to be merged automatically when the pipeline succeeds')
- expect(page).to have_link('Cancel automatic merge')
+ expect(page).to have_link('Cancel')
end
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 0cb4107c21d..d9b5ec17a4a 100644
--- a/spec/features/merge_request/user_sees_merge_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -445,7 +445,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
wait_for_requests
expect(page).not_to have_button('Merge')
- expect(page).to have_content('This merge request is in the process of being merged')
+ expect(page).to have_content('Merging!')
end
end
diff --git a/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb b/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb
index cbd68025b50..a764dd97878 100644
--- a/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb
+++ b/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe 'Merge request > User sees MR from deleted forked project', :js d
end
before do
- MergeRequests::MergeService.new(project, user).execute(merge_request)
+ MergeRequests::MergeService.new(project: project, current_user: user).execute(merge_request)
forked_project.destroy!
sign_in(user)
visit project_merge_request_path(project, merge_request)
diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb
index a5047c8d550..2d8fe10b987 100644
--- a/spec/features/merge_request/user_sees_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_pipelines_spec.rb
@@ -239,7 +239,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js do
threads << Thread.new do
Sidekiq::Worker.skipping_transaction_check do
- @merge_request = MergeRequests::CreateService.new(project, user, merge_request_params).execute
+ @merge_request = MergeRequests::CreateService.new(project: project, current_user: user, params: merge_request_params).execute
end
end
diff --git a/spec/features/merge_request/user_sees_wip_help_message_spec.rb b/spec/features/merge_request/user_sees_wip_help_message_spec.rb
index 204df5b3995..0a6a3d82ee0 100644
--- a/spec/features/merge_request/user_sees_wip_help_message_spec.rb
+++ b/spec/features/merge_request/user_sees_wip_help_message_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe 'Merge request > User sees draft help message' do
'It looks like you have some draft commits in this branch'
)
expect(page).to have_text(
- "Start the title with Draft: or WIP: to prevent a merge request that is a \
+ "Start the title with Draft: to prevent a merge request that is a \
work in progress from being merged before it's ready."
)
end
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 f1b44010f63..073706cf9d8 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
@@ -76,7 +76,7 @@ RSpec.describe 'User views an open merge request' do
it 'does not show diverged commits count' do
page.within('.mr-source-target') do
- expect(page).not_to have_content(/([0-9]+ commit[s]? behind)/)
+ expect(page).not_to have_content(/([0-9]+ commits? behind)/)
end
end
end
diff --git a/spec/features/merge_requests/user_mass_updates_spec.rb b/spec/features/merge_requests/user_mass_updates_spec.rb
index 179bf84a729..0fe69c5ca5b 100644
--- a/spec/features/merge_requests/user_mass_updates_spec.rb
+++ b/spec/features/merge_requests/user_mass_updates_spec.rb
@@ -44,7 +44,7 @@ RSpec.describe 'Merge requests > User mass updates', :js do
click_button 'Edit merge requests'
- expect(page).not_to have_css('.js-issue-status')
+ expect(page).not_to have_button 'Select status'
end
end
@@ -57,9 +57,7 @@ RSpec.describe 'Merge requests > User mass updates', :js do
it 'updates merge request with assignee' do
change_assignee(user.name)
- page.within('.merge-request .controls') do
- expect(find('.author-link')["title"]).to have_content(user.name)
- end
+ expect(find('.merge-request')).to have_link "Assigned to #{user.name}"
end
end
@@ -72,7 +70,7 @@ RSpec.describe 'Merge requests > User mass updates', :js do
it 'removes assignee from the merge request' do
change_assignee('Unassigned')
- expect(find('.merge-request .controls')).not_to have_css('.author-link')
+ expect(find('.merge-request')).not_to have_link "Assigned to #{user.name}"
end
end
end
@@ -109,35 +107,33 @@ RSpec.describe 'Merge requests > User mass updates', :js do
def change_status(text)
click_button 'Edit merge requests'
- find('#check-all-issues').click
- find('.js-issue-status').click
- find('.dropdown-menu-status a', text: text).click
+ check 'Select all'
+ click_button 'Select status'
+ click_link text
click_update_merge_requests_button
end
def change_assignee(text)
click_button 'Edit merge requests'
- find('#check-all-issues').click
- find('.js-update-assignee').click
- wait_for_requests
-
- page.within '.dropdown-menu-user' do
+ check 'Select all'
+ within 'aside[aria-label="Bulk update"]' do
+ click_button 'Select assignee'
+ wait_for_requests
click_link text
end
-
click_update_merge_requests_button
end
def change_milestone(text)
click_button 'Edit merge requests'
- find('#check-all-issues').click
- find('.issues-bulk-update .js-milestone-select').click
- find('.dropdown-menu-milestone a', text: text).click
+ check 'Select all'
+ click_button 'Select milestone'
+ click_link text
click_update_merge_requests_button
end
def click_update_merge_requests_button
- find('.update-selected-issues').click
+ click_button 'Update all'
wait_for_requests
end
end
diff --git a/spec/features/monitor_sidebar_link_spec.rb b/spec/features/monitor_sidebar_link_spec.rb
new file mode 100644
index 00000000000..bb5e581a034
--- /dev/null
+++ b/spec/features/monitor_sidebar_link_spec.rb
@@ -0,0 +1,145 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
+ let_it_be_with_reload(:project) { create(:project, :internal, :repository) }
+
+ let(:user) { create(:user) }
+ let(:access_level) { ProjectFeature::PUBLIC }
+ let(:role) { nil }
+
+ before do
+ project.add_role(user, role) if role
+ project.project_feature.update_attribute(:operations_access_level, access_level)
+
+ sign_in(user)
+ visit project_issues_path(project)
+ end
+
+ shared_examples 'shows Monitor menu based on the access level' do
+ context 'when operations project feature is PRIVATE' do
+ let(:access_level) { ProjectFeature::PRIVATE }
+
+ it 'shows the `Monitor` menu' do
+ expect(page).to have_selector('a.shortcuts-monitor', text: 'Monitor')
+ end
+ end
+
+ context 'when operations project feature is DISABLED' do
+ let(:access_level) { ProjectFeature::DISABLED }
+
+ it 'does not show the `Monitor` menu' do
+ expect(page).not_to have_selector('a.shortcuts-monitor')
+ end
+ end
+ end
+
+ context 'user is not a member' do
+ it 'has the correct `Monitor` menu items', :aggregate_failures do
+ expect(page).to have_selector('a.shortcuts-monitor', text: 'Monitor')
+ expect(page).to have_link('Incidents', href: project_incidents_path(project))
+ expect(page).to have_link('Environments', href: project_environments_path(project))
+
+ expect(page).not_to have_link('Metrics', href: project_metrics_dashboard_path(project))
+ expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project))
+ expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project))
+ expect(page).not_to have_link('Product Analytics', href: project_product_analytics_path(project))
+ expect(page).not_to have_link('Serverless', href: project_serverless_functions_path(project))
+ expect(page).not_to have_link('Logs', href: project_logs_path(project))
+ expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
+ end
+
+ context 'when operations project feature is PRIVATE' do
+ let(:access_level) { ProjectFeature::PRIVATE }
+
+ it 'does not show the `Monitor` menu' do
+ expect(page).not_to have_selector('a.shortcuts-monitor')
+ end
+ end
+
+ context 'when operations project feature is DISABLED' do
+ let(:access_level) { ProjectFeature::DISABLED }
+
+ it 'does not show the `Operations` menu' do
+ expect(page).not_to have_selector('a.shortcuts-monitor')
+ end
+ end
+ end
+
+ context 'user has guest role' do
+ let(:role) { :guest }
+
+ it 'has the correct `Monitor` menu items' do
+ expect(page).to have_selector('a.shortcuts-monitor', text: 'Monitor')
+ expect(page).to have_link('Incidents', href: project_incidents_path(project))
+ expect(page).to have_link('Environments', href: project_environments_path(project))
+
+ expect(page).not_to have_link('Metrics', href: project_metrics_dashboard_path(project))
+ expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project))
+ expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project))
+ expect(page).not_to have_link('Product Analytics', href: project_product_analytics_path(project))
+ expect(page).not_to have_link('Serverless', href: project_serverless_functions_path(project))
+ expect(page).not_to have_link('Logs', href: project_logs_path(project))
+ expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
+ end
+
+ it_behaves_like 'shows Monitor menu based on the access level'
+ end
+
+ context 'user has reporter role' do
+ let(:role) { :reporter }
+
+ it 'has the correct `Monitor` menu items' do
+ expect(page).to have_link('Metrics', href: project_metrics_dashboard_path(project))
+ expect(page).to have_link('Incidents', href: project_incidents_path(project))
+ expect(page).to have_link('Environments', href: project_environments_path(project))
+ expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project))
+ expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project))
+
+ expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project))
+ expect(page).not_to have_link('Serverless', href: project_serverless_functions_path(project))
+ expect(page).not_to have_link('Logs', href: project_logs_path(project))
+ expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
+ end
+
+ it_behaves_like 'shows Monitor menu based on the access level'
+ end
+
+ context 'user has developer role' do
+ let(:role) { :developer }
+
+ it 'has the correct `Monitor` menu items' do
+ expect(page).to have_link('Metrics', href: project_metrics_dashboard_path(project))
+ expect(page).to have_link('Alerts', href: project_alert_management_index_path(project))
+ expect(page).to have_link('Incidents', href: project_incidents_path(project))
+ expect(page).to have_link('Environments', href: project_environments_path(project))
+ expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project))
+ expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project))
+ expect(page).to have_link('Logs', href: project_logs_path(project))
+
+ expect(page).not_to have_link('Serverless', href: project_serverless_functions_path(project))
+ expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
+ end
+
+ it_behaves_like 'shows Monitor menu based on the access level'
+ end
+
+ context 'user has maintainer role' do
+ let(:role) { :maintainer }
+
+ it 'has the correct `Monitor` menu items' do
+ expect(page).to have_link('Metrics', href: project_metrics_dashboard_path(project))
+ expect(page).to have_link('Alerts', href: project_alert_management_index_path(project))
+ expect(page).to have_link('Incidents', href: project_incidents_path(project))
+ expect(page).to have_link('Environments', href: project_environments_path(project))
+ expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project))
+ expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project))
+ expect(page).to have_link('Serverless', href: project_serverless_functions_path(project))
+ expect(page).to have_link('Logs', href: project_logs_path(project))
+ expect(page).to have_link('Kubernetes', href: project_clusters_path(project))
+ end
+
+ it_behaves_like 'shows Monitor menu based on the access level'
+ end
+end
diff --git a/spec/features/operations_sidebar_link_spec.rb b/spec/features/operations_sidebar_link_spec.rb
deleted file mode 100644
index 798f9092db0..00000000000
--- a/spec/features/operations_sidebar_link_spec.rb
+++ /dev/null
@@ -1,144 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Operations dropdown sidebar', :aggregate_failures do
- let_it_be_with_reload(:project) { create(:project, :internal, :repository) }
- let(:user) { create(:user) }
- let(:access_level) { ProjectFeature::PUBLIC }
- let(:role) { nil }
-
- before do
- project.add_role(user, role) if role
- project.project_feature.update_attribute(:operations_access_level, access_level)
-
- sign_in(user)
- visit project_issues_path(project)
- end
-
- shared_examples 'shows Operations menu based on the access level' do
- context 'when operations project feature is PRIVATE' do
- let(:access_level) { ProjectFeature::PRIVATE }
-
- it 'shows the `Operations` menu' do
- expect(page).to have_selector('a.shortcuts-operations', text: 'Operations')
- end
- end
-
- context 'when operations project feature is DISABLED' do
- let(:access_level) { ProjectFeature::DISABLED }
-
- it 'does not show the `Operations` menu' do
- expect(page).not_to have_selector('a.shortcuts-operations')
- end
- end
- end
-
- context 'user is not a member' do
- it 'has the correct `Operations` menu items', :aggregate_failures do
- expect(page).to have_selector('a.shortcuts-operations', text: 'Operations')
- expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project))
- expect(page).to have_link(title: 'Environments', href: project_environments_path(project))
-
- expect(page).not_to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project))
- expect(page).not_to have_link(title: 'Alerts', href: project_alert_management_index_path(project))
- expect(page).not_to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project))
- expect(page).not_to have_link(title: 'Product Analytics', href: project_product_analytics_path(project))
- expect(page).not_to have_link(title: 'Serverless', href: project_serverless_functions_path(project))
- expect(page).not_to have_link(title: 'Logs', href: project_logs_path(project))
- expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project))
- end
-
- context 'when operations project feature is PRIVATE' do
- let(:access_level) { ProjectFeature::PRIVATE }
-
- it 'does not show the `Operations` menu' do
- expect(page).not_to have_selector('a.shortcuts-operations')
- end
- end
-
- context 'when operations project feature is DISABLED' do
- let(:access_level) { ProjectFeature::DISABLED }
-
- it 'does not show the `Operations` menu' do
- expect(page).not_to have_selector('a.shortcuts-operations')
- end
- end
- end
-
- context 'user has guest role' do
- let(:role) { :guest }
-
- it 'has the correct `Operations` menu items' do
- expect(page).to have_selector('a.shortcuts-operations', text: 'Operations')
- expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project))
- expect(page).to have_link(title: 'Environments', href: project_environments_path(project))
-
- expect(page).not_to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project))
- expect(page).not_to have_link(title: 'Alerts', href: project_alert_management_index_path(project))
- expect(page).not_to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project))
- expect(page).not_to have_link(title: 'Product Analytics', href: project_product_analytics_path(project))
- expect(page).not_to have_link(title: 'Serverless', href: project_serverless_functions_path(project))
- expect(page).not_to have_link(title: 'Logs', href: project_logs_path(project))
- expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project))
- end
-
- it_behaves_like 'shows Operations menu based on the access level'
- end
-
- context 'user has reporter role' do
- let(:role) { :reporter }
-
- it 'has the correct `Operations` menu items' do
- expect(page).to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project))
- expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project))
- expect(page).to have_link(title: 'Environments', href: project_environments_path(project))
- expect(page).to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project))
- expect(page).to have_link(title: 'Product Analytics', href: project_product_analytics_path(project))
-
- expect(page).not_to have_link(title: 'Alerts', href: project_alert_management_index_path(project))
- expect(page).not_to have_link(title: 'Serverless', href: project_serverless_functions_path(project))
- expect(page).not_to have_link(title: 'Logs', href: project_logs_path(project))
- expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project))
- end
-
- it_behaves_like 'shows Operations menu based on the access level'
- end
-
- context 'user has developer role' do
- let(:role) { :developer }
-
- it 'has the correct `Operations` menu items' do
- expect(page).to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project))
- expect(page).to have_link(title: 'Alerts', href: project_alert_management_index_path(project))
- expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project))
- expect(page).to have_link(title: 'Environments', href: project_environments_path(project))
- expect(page).to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project))
- expect(page).to have_link(title: 'Product Analytics', href: project_product_analytics_path(project))
- expect(page).to have_link(title: 'Logs', href: project_logs_path(project))
-
- expect(page).not_to have_link(title: 'Serverless', href: project_serverless_functions_path(project))
- expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project))
- end
-
- it_behaves_like 'shows Operations menu based on the access level'
- end
-
- context 'user has maintainer role' do
- let(:role) { :maintainer }
-
- it 'has the correct `Operations` menu items' do
- expect(page).to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project))
- expect(page).to have_link(title: 'Alerts', href: project_alert_management_index_path(project))
- expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project))
- expect(page).to have_link(title: 'Environments', href: project_environments_path(project))
- expect(page).to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project))
- expect(page).to have_link(title: 'Product Analytics', href: project_product_analytics_path(project))
- expect(page).to have_link(title: 'Serverless', href: project_serverless_functions_path(project))
- expect(page).to have_link(title: 'Logs', href: project_logs_path(project))
- expect(page).to have_link(title: 'Kubernetes', href: project_clusters_path(project))
- end
-
- it_behaves_like 'shows Operations menu based on the access level'
- end
-end
diff --git a/spec/features/populate_new_pipeline_vars_with_params_spec.rb b/spec/features/populate_new_pipeline_vars_with_params_spec.rb
index 37fea5331a3..937f99558ad 100644
--- a/spec/features/populate_new_pipeline_vars_with_params_spec.rb
+++ b/spec/features/populate_new_pipeline_vars_with_params_spec.rb
@@ -8,7 +8,6 @@ RSpec.describe "Populate new pipeline CI variables with url params", :js do
let(:page_path) { new_project_pipeline_path(project) }
before do
- stub_feature_flags(new_pipeline_form: false)
sign_in(user)
project.add_maintainer(user)
@@ -16,18 +15,18 @@ RSpec.describe "Populate new pipeline CI variables with url params", :js do
end
it "var[key1]=value1 populates env_var variable correctly" do
- page.within('.ci-variable-list .js-row:nth-child(1)') do
- expect(find('.js-ci-variable-input-variable-type').value).to eq('env_var')
- expect(find('.js-ci-variable-input-key').value).to eq('key1')
- expect(find('.js-ci-variable-input-value').text).to eq('value1')
+ page.within(all("[data-testid='ci-variable-row']")[0]) do
+ expect(find("[data-testid='pipeline-form-ci-variable-type']").value).to eq('env_var')
+ expect(find("[data-testid='pipeline-form-ci-variable-key']").value).to eq('key1')
+ expect(find("[data-testid='pipeline-form-ci-variable-value']").value).to eq('value1')
end
end
it "file_var[key2]=value2 populates file variable correctly" do
- page.within('.ci-variable-list .js-row:nth-child(2)') do
- expect(find('.js-ci-variable-input-variable-type').value).to eq('file')
- expect(find('.js-ci-variable-input-key').value).to eq('key2')
- expect(find('.js-ci-variable-input-value').text).to eq('value2')
+ page.within(all("[data-testid='ci-variable-row']")[1]) do
+ expect(find("[data-testid='pipeline-form-ci-variable-type']").value).to eq('file')
+ expect(find("[data-testid='pipeline-form-ci-variable-key']").value).to eq('key2')
+ expect(find("[data-testid='pipeline-form-ci-variable-value']").value).to eq('value2')
end
end
end
diff --git a/spec/features/profiles/chat_names_spec.rb b/spec/features/profiles/chat_names_spec.rb
index ca888018cad..6270fa7347d 100644
--- a/spec/features/profiles/chat_names_spec.rb
+++ b/spec/features/profiles/chat_names_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe 'Profile > Chat' do
let(:user) { create(:user) }
- let(:service) { create(:service) }
+ let(:integration) { create(:service) }
before do
sign_in(user)
@@ -15,7 +15,7 @@ RSpec.describe 'Profile > Chat' do
{ team_id: 'T00', team_domain: 'my_chat_team', user_id: 'U01', user_name: 'my_chat_user' }
end
- let!(:authorize_url) { ChatNames::AuthorizeUserService.new(service, params).execute }
+ let!(:authorize_url) { ChatNames::AuthorizeUserService.new(integration, params).execute }
let(:authorize_path) { URI.parse(authorize_url).request_uri }
before do
@@ -60,7 +60,7 @@ RSpec.describe 'Profile > Chat' do
end
describe 'visits chat accounts' do
- let!(:chat_name) { create(:chat_name, user: user, service: service) }
+ let!(:chat_name) { create(:chat_name, user: user, integration: integration) }
before do
visit profile_chat_names_path
diff --git a/spec/features/profiles/user_edit_preferences_spec.rb b/spec/features/profiles/user_edit_preferences_spec.rb
index 3129e4bd952..c724de04043 100644
--- a/spec/features/profiles/user_edit_preferences_spec.rb
+++ b/spec/features/profiles/user_edit_preferences_spec.rb
@@ -1,10 +1,16 @@
# frozen_string_literal: true
+
require 'spec_helper'
RSpec.describe 'User edit preferences profile', :js do
+ include StubLanguagesTranslationPercentage
+
+ # Empty value doesn't change the levels
+ let(:language_percentage_levels) { nil }
let(:user) { create(:user) }
before do
+ stub_languages_translation_percentage(language_percentage_levels)
stub_feature_flags(user_time_settings: true)
sign_in(user)
visit(profile_preferences_path)
@@ -63,17 +69,4 @@ RSpec.describe 'User edit preferences profile', :js do
expect(page).to have_content('Failed to save preferences.')
end
end
-
- describe 'User language' do
- let(:user) { create(:user, preferred_language: :es) }
-
- it 'shows the user preferred language by default' do
- expect(page).to have_select(
- 'user[preferred_language]',
- selected: 'Spanish - español',
- options: Gitlab::I18n.selectable_locales.values,
- visible: :all
- )
- end
- end
end
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
index 57f7c7878e3..dddca15ae24 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -232,17 +232,6 @@ RSpec.describe 'User edit profile' do
expect(page.find('.issuable-assignees')).to have_content("#{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)
- visit root_path(user)
- end
-
- it 'does not display the availability checkbox' do
- expect(page).not_to have_css('[data-testid="user-availability-checkbox"]')
- end
- end
end
context 'user menu' do
@@ -487,19 +476,6 @@ RSpec.describe 'User edit profile' do
expect(first_note).not_to have_css('.user-status-emoji')
end
end
-
- context 'with set_user_availability_status feature flag disabled' do
- before do
- stub_feature_flags(set_user_availability_status: false)
- visit root_path(user)
- end
-
- it 'does not display the availability checkbox' do
- open_user_status_modal
-
- expect(page).not_to have_css('[data-testid="user-availability-checkbox"]')
- end
- end
end
context 'User time preferences', :js do
diff --git a/spec/features/profiles/user_visits_notifications_tab_spec.rb b/spec/features/profiles/user_visits_notifications_tab_spec.rb
index 939e791c75d..e960cc76219 100644
--- a/spec/features/profiles/user_visits_notifications_tab_spec.rb
+++ b/spec/features/profiles/user_visits_notifications_tab_spec.rb
@@ -12,6 +12,14 @@ RSpec.describe 'User visits the notifications tab', :js do
visit(profile_notifications_path)
end
+ it 'turns on the receive product marketing emails setting' do
+ expect(page).to have_content('Notifications')
+
+ expect do
+ check 'Receive product marketing emails'
+ end.to change { user.reload.email_opted_in }.to(true)
+ end
+
it 'changes the project notifications setting' do
expect(page).to have_content('Notifications')
diff --git a/spec/features/project_variables_spec.rb b/spec/features/project_variables_spec.rb
index 327d8133411..62565eaabe1 100644
--- a/spec/features/project_variables_spec.rb
+++ b/spec/features/project_variables_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe 'Project variables', :js do
it_behaves_like 'variable list'
it 'adds a new variable with an environment scope' do
- click_button('Add Variable')
+ click_button('Add variable')
page.within('#add-ci-variable') do
find('[data-qa-selector="ci_variable_key_field"] input').set('akey')
diff --git a/spec/features/projects/active_tabs_spec.rb b/spec/features/projects/active_tabs_spec.rb
index 9de43e7d18c..96a321037a9 100644
--- a/spec/features/projects/active_tabs_spec.rb
+++ b/spec/features/projects/active_tabs_spec.rb
@@ -3,11 +3,11 @@
require 'spec_helper'
RSpec.describe 'Project active tab' do
- let(:user) { create :user }
- let(:project) { create(:project, :repository) }
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:user) { project.owner }
before do
- project.add_maintainer(user)
sign_in(user)
end
@@ -18,15 +18,28 @@ RSpec.describe 'Project active tab' do
end
context 'on project Home' do
- before do
- visit project_path(project)
+ context 'when feature flag :sidebar_refactor is enabled' do
+ before do
+ visit project_path(project)
+ end
+
+ it_behaves_like 'page has active tab', 'Project'
end
- it_behaves_like 'page has active tab', 'Project'
- it_behaves_like 'page has active sub tab', 'Details'
+ context 'when feature flag :sidebar_refactor is disabled' do
+ before do
+ stub_feature_flags(sidebar_refactor: false)
+
+ visit project_path(project)
+ end
+
+ it_behaves_like 'page has active tab', 'Project'
+ it_behaves_like 'page has active sub tab', 'Details'
+ end
context 'on project Home/Activity' do
before do
+ visit project_path(project)
click_tab('Activity')
end
@@ -56,20 +69,37 @@ RSpec.describe 'Project active tab' do
end
context 'on project Issues' do
+ let(:feature_flag_value) { true }
+
before do
+ stub_feature_flags(sidebar_refactor: feature_flag_value)
+
visit project_issues_path(project)
end
it_behaves_like 'page has active tab', 'Issues'
- %w(Milestones Labels).each do |sub_menu|
- context "on project Issues/#{sub_menu}" do
- before do
- click_tab(sub_menu)
- end
+ context "on project Issues/Milestones" do
+ before do
+ click_tab('Milestones')
+ end
- it_behaves_like 'page has active tab', 'Issues'
- it_behaves_like 'page has active sub tab', sub_menu
+ it_behaves_like 'page has active tab', 'Issues'
+ it_behaves_like 'page has active sub tab', 'Milestones'
+ end
+
+ context 'when feature flag is disabled' do
+ let(:feature_flag_value) { false }
+
+ %w(Milestones Labels).each do |sub_menu|
+ context "on project Issues/#{sub_menu}" do
+ before do
+ click_tab(sub_menu)
+ end
+
+ it_behaves_like 'page has active tab', 'Issues'
+ it_behaves_like 'page has active sub tab', sub_menu
+ end
end
end
end
diff --git a/spec/features/projects/badges/pipeline_badge_spec.rb b/spec/features/projects/badges/pipeline_badge_spec.rb
index c24ab5c4058..bfc924b5d9b 100644
--- a/spec/features/projects/badges/pipeline_badge_spec.rb
+++ b/spec/features/projects/badges/pipeline_badge_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Pipeline Badge' do
let_it_be(:project) { create(:project, :repository, :public) }
+
let(:ref) { project.default_branch }
context 'when the project has a pipeline' do
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
index 7c564d76f70..3598aa2f423 100644
--- a/spec/features/projects/blobs/blob_show_spec.rb
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -119,6 +119,81 @@ RSpec.describe 'File blob', :js do
end
end
+ context 'when ref switch' do
+ def switch_ref_to(ref_name)
+ first('.qa-branches-select').click
+
+ page.within '.project-refs-form' do
+ click_link ref_name
+ wait_for_requests
+ end
+ end
+
+ it 'displays single highlighted line number of different ref' do
+ visit_blob('files/js/application.js', anchor: 'L1')
+
+ switch_ref_to('feature')
+
+ page.within '.blob-content' do
+ expect(find_by_id('LC1')[:class]).to include("hll")
+ end
+ end
+
+ it 'displays multiple highlighted line numbers of different ref' do
+ visit_blob('files/js/application.js', anchor: 'L1-3')
+
+ switch_ref_to('feature')
+
+ page.within '.blob-content' do
+ expect(find_by_id('LC1')[:class]).to include("hll")
+ expect(find_by_id('LC2')[:class]).to include("hll")
+ expect(find_by_id('LC3')[:class]).to include("hll")
+ end
+ end
+
+ it 'displays no highlighted number of different ref' do
+ Files::UpdateService.new(
+ project,
+ project.owner,
+ commit_message: 'Update',
+ start_branch: 'feature',
+ branch_name: 'feature',
+ file_path: 'files/js/application.js',
+ file_content: 'new content'
+ ).execute
+
+ project.commit('feature').diffs.diff_files.first
+
+ visit_blob('files/js/application.js', anchor: 'L3')
+ switch_ref_to('feature')
+
+ page.within '.blob-content' do
+ expect(page).not_to have_css('.hll')
+ end
+ end
+
+ context 'sucessfully change ref of similar name' do
+ before do
+ project.repository.create_branch('dev')
+ project.repository.create_branch('development')
+ end
+
+ it 'switch ref from longer to shorter ref name' do
+ visit_blob('files/js/application.js', ref: 'development')
+ switch_ref_to('dev')
+
+ expect(page.find('.file-title-name').text).to eq('application.js')
+ end
+
+ it 'switch ref from shorter to longer ref name' do
+ visit_blob('files/js/application.js', ref: 'dev')
+ switch_ref_to('development')
+
+ expect(page.find('.file-title-name').text).to eq('application.js')
+ end
+ end
+ end
+
context 'visiting with a line number anchor' do
before do
visit_blob('files/markdown/ruby-style-guide.md', anchor: 'L1')
diff --git a/spec/features/projects/branches/user_deletes_branch_spec.rb b/spec/features/projects/branches/user_deletes_branch_spec.rb
index bebb4bb679b..53994ec018e 100644
--- a/spec/features/projects/branches/user_deletes_branch_spec.rb
+++ b/spec/features/projects/branches/user_deletes_branch_spec.rb
@@ -4,6 +4,7 @@ require "spec_helper"
RSpec.describe "User deletes branch", :js do
let_it_be(:user) { create(:user) }
+
let(:project) { create(:project, :repository) }
before do
diff --git a/spec/features/projects/commit/builds_spec.rb b/spec/features/projects/commit/builds_spec.rb
index 00ec9d49a10..7b10f72006f 100644
--- a/spec/features/projects/commit/builds_spec.rb
+++ b/spec/features/projects/commit/builds_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe 'project commit pipelines', :js do
wait_for_requests
page.within('.merge-request-info') do
- expect(page).not_to have_selector '.spinner'
+ expect(page).not_to have_selector '.gl-spinner'
expect(page).to have_content 'No related merge requests found'
end
end
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index cd944436228..fce9fa4fb62 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
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) }
diff --git a/spec/features/projects/commit/user_comments_on_commit_spec.rb b/spec/features/projects/commit/user_comments_on_commit_spec.rb
index 0fa4975bb25..6997c2d8338 100644
--- a/spec/features/projects/commit/user_comments_on_commit_spec.rb
+++ b/spec/features/projects/commit/user_comments_on_commit_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe "User comments on commit", :js do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
+
let(:comment_text) { "XML attached" }
before_all do
diff --git a/spec/features/projects/commit/user_reverts_commit_spec.rb b/spec/features/projects/commit/user_reverts_commit_spec.rb
index ad327b86aa7..1c6cf5eb258 100644
--- a/spec/features/projects/commit/user_reverts_commit_spec.rb
+++ b/spec/features/projects/commit/user_reverts_commit_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe 'User reverts a commit', :js do
include RepoHelpers
let_it_be(:user) { create(:user) }
+
let!(:project) { create_default(:project, :repository, namespace: user.namespace) }
before do
diff --git a/spec/features/projects/commit/user_views_user_status_on_commit_spec.rb b/spec/features/projects/commit/user_views_user_status_on_commit_spec.rb
index 89ff2f4b26d..cc3c70e66ce 100644
--- a/spec/features/projects/commit/user_views_user_status_on_commit_spec.rb
+++ b/spec/features/projects/commit/user_views_user_status_on_commit_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe 'Project > Commit > View user status' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
+
let(:commit_author) { create(:user, email: sample_commit.author_email) }
before do
diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb
index 64e9968061c..bc3ef2af9b0 100644
--- a/spec/features/projects/compare_spec.rb
+++ b/spec/features/projects/compare_spec.rb
@@ -118,6 +118,34 @@ RSpec.describe "Compare", :js do
end
end
end
+
+ context "pagination" do
+ before do
+ stub_const("Projects::CompareController::COMMIT_DIFFS_PER_PAGE", 1)
+ end
+
+ it "shows an adjusted count for changed files on this page" do
+ visit project_compare_index_path(project, from: "feature", to: "master")
+
+ click_button('Compare')
+
+ expect(page).to have_content("Showing 1 changed file")
+ end
+
+ it "shows commits list only on the first page" do
+ visit project_compare_index_path(project, from: "feature", to: "master")
+ click_button('Compare')
+
+ expect(page).to have_content 'Commits (29)'
+
+ # go to the second page
+ within(".files .gl-pagination") do
+ click_on("2")
+ end
+
+ expect(page).not_to have_content 'Commits (29)'
+ end
+ end
end
describe "tags" do
diff --git a/spec/features/projects/confluence/user_views_confluence_page_spec.rb b/spec/features/projects/confluence/user_views_confluence_page_spec.rb
index d39c97291db..7ec724ed55d 100644
--- a/spec/features/projects/confluence/user_views_confluence_page_spec.rb
+++ b/spec/features/projects/confluence/user_views_confluence_page_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'User views the Confluence page' do
let_it_be(:user) { create(:user) }
+
let(:project) { create(:project, :public) }
before do
diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb
index 6218578cac6..bf705cf875b 100644
--- a/spec/features/projects/deploy_keys_spec.rb
+++ b/spec/features/projects/deploy_keys_spec.rb
@@ -19,10 +19,11 @@ RSpec.describe 'Project deploy keys', :js do
it 'removes association between project and deploy key' do
visit project_settings_repository_path(project)
- page.within(find('.qa-deploy-keys-settings')) do
+ page.within(find('.rspec-deploy-keys-settings')) do
expect(page).to have_selector('.deploy-key', count: 1)
- accept_confirm { find('[data-testid="remove-icon"]').click }
+ click_button 'Remove'
+ click_button 'Remove deploy key'
wait_for_requests
diff --git a/spec/features/projects/diffs/diff_show_spec.rb b/spec/features/projects/diffs/diff_show_spec.rb
index 747277e2562..e47f36c4b7a 100644
--- a/spec/features/projects/diffs/diff_show_spec.rb
+++ b/spec/features/projects/diffs/diff_show_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Diff file viewer', :js do
+RSpec.describe 'Diff file viewer', :js, :with_clean_rails_cache do
let(:project) { create(:project, :public, :repository) }
def visit_commit(sha, anchor: nil)
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index ab82a4750d3..363fe8c35fe 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -54,17 +54,30 @@ RSpec.describe 'Edit Project Settings' do
end
context 'When external issue tracker is enabled and issues disabled on project settings' do
- it 'hides issues tab and show labels tab' do
+ before do
project.issues_enabled = false
project.save!
allow_next_instance_of(Project) do |instance|
allow(instance).to receive(:external_issue_tracker).and_return(JiraService.new)
end
+ end
+ it 'hides issues tab' do
visit project_path(project)
expect(page).not_to have_selector('.shortcuts-issues')
- expect(page).to have_selector('.shortcuts-labels')
+ expect(page).not_to have_selector('.shortcuts-labels')
+ end
+
+ context 'when feature flag :sidebar_refactor is disabled' do
+ it 'hides issues tab and show labels tab' do
+ stub_feature_flags(sidebar_refactor: false)
+
+ visit project_path(project)
+
+ expect(page).not_to have_selector('.shortcuts-issues')
+ expect(page).to have_selector('.shortcuts-labels')
+ end
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
deleted file mode 100644
index cd796d45aba..00000000000
--- a/spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file' do
- include Spec::Support::Helpers::Features::EditorLiteSpecHelpers
-
- let_it_be(:namespace) { create(:namespace) }
- let(:project) { create(:project, :repository, namespace: namespace) }
-
- before do
- sign_in project.owner
- stub_experiment(ci_syntax_templates_b: experiment_active)
- stub_experiment_for_subject(ci_syntax_templates_b: in_experiment_group)
-
- visit project_new_blob_path(project, 'master', file_name: '.gitlab-ci.yml')
- end
-
- context 'when experiment is not active' do
- let(:experiment_active) { false }
- let(:in_experiment_group) { false }
-
- it 'does not show the "Learn CI/CD syntax" template dropdown' do
- expect(page).not_to have_css('.gitlab-ci-syntax-yml-selector')
- end
- end
-
- context 'when experiment is active' do
- let(:experiment_active) { true }
-
- context 'when the user is in the control group' do
- let(:in_experiment_group) { false }
-
- it 'does not show the "Learn CI/CD syntax" template dropdown' do
- expect(page).not_to have_css('.gitlab-ci-syntax-yml-selector')
- end
- end
-
- context 'when the user is in the experimental group' do
- let(:in_experiment_group) { true }
-
- 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
-
- wait_for_requests
-
- within '.gitlab-ci-syntax-yml-selector' do
- find('.dropdown-input-field').set('Artifacts example')
- find('.dropdown-content .is-focused', text: 'Artifacts example').click
- end
-
- wait_for_requests
-
- expect(page).to have_css('.gitlab-ci-syntax-yml-selector .dropdown-toggle-text', text: 'Learn CI/CD syntax')
- expect(editor_get_value).to have_content('You can use artifacts to pass data to jobs in later stages.')
- end
-
- context 'when the group is created longer than 90 days ago' do
- let(:namespace) { create(:namespace, created_at: 91.days.ago) }
-
- it 'does not show the "Learn CI/CD syntax" template dropdown' do
- expect(page).not_to have_css('.gitlab-ci-syntax-yml-selector')
- end
- end
- end
- end
-end
diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb
index c18ff9ddbbc..453cc14c267 100644
--- a/spec/features/projects/files/user_edits_files_spec.rb
+++ b/spec/features/projects/files/user_edits_files_spec.rb
@@ -131,8 +131,8 @@ RSpec.describe 'Projects > Files > User edits files', :js do
expect(page).to have_selector(:link_or_button, 'Fork')
expect(page).to have_selector(:link_or_button, 'Cancel')
expect(page).to have_content(
- "You're not allowed to edit files in this project directly. "\
- "Please fork this project, make your changes there, and submit a merge request."
+ "You can’t edit files directly in this project. "\
+ "Fork this project and submit a merge request with your changes."
)
end
diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb
index 2b7ea70fe5a..9a6d1961a02 100644
--- a/spec/features/projects/fork_spec.rb
+++ b/spec/features/projects/fork_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe 'Project fork' do
include ProjectForksHelper
let(:user) { create(:user) }
- let(:project) { create(:project, :public, :repository) }
+ let(:project) { create(:project, :public, :repository, description: 'some description') }
before do
sign_in(user)
@@ -228,7 +228,7 @@ RSpec.describe 'Project fork' do
click_link 'Fork'
page.within('.fork-thumbnail-container') do
- expect(page).to have_css('div.identicon')
+ expect(page).to have_css('span.identicon')
end
end
diff --git a/spec/features/projects/graph_spec.rb b/spec/features/projects/graph_spec.rb
index 72df84bf905..7e039a087c7 100644
--- a/spec/features/projects/graph_spec.rb
+++ b/spec/features/projects/graph_spec.rb
@@ -75,7 +75,7 @@ RSpec.describe 'Project Graph', :js do
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'
+ expect(page).to have_content 'Pipeline durations for the last 30 commits'
end
end
end
diff --git a/spec/features/projects/services/user_activates_asana_spec.rb b/spec/features/projects/integrations/user_activates_asana_spec.rb
index cf2290383e8..cf2290383e8 100644
--- a/spec/features/projects/services/user_activates_asana_spec.rb
+++ b/spec/features/projects/integrations/user_activates_asana_spec.rb
diff --git a/spec/features/projects/services/user_activates_assembla_spec.rb b/spec/features/projects/integrations/user_activates_assembla_spec.rb
index 63cc424a641..63cc424a641 100644
--- a/spec/features/projects/services/user_activates_assembla_spec.rb
+++ b/spec/features/projects/integrations/user_activates_assembla_spec.rb
diff --git a/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb b/spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb
index 91db375be3a..91db375be3a 100644
--- a/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb
+++ b/spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb
diff --git a/spec/features/projects/issues/design_management/user_views_design_images_spec.rb b/spec/features/projects/issues/design_management/user_views_design_images_spec.rb
index 4a4c33cb881..c3aefe05f75 100644
--- a/spec/features/projects/issues/design_management/user_views_design_images_spec.rb
+++ b/spec/features/projects/issues/design_management/user_views_design_images_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe 'Users views raw design image files' do
let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:design) { create(:design, :with_file, issue: issue, versions_count: 2) }
+
let(:newest_version) { design.versions.ordered.first }
let(:oldest_version) { design.versions.ordered.last }
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 18a6ad12240..a1416f3f563 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -136,7 +136,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
visit project_job_path(project, job)
wait_for_requests
- expect(page).to have_selector('.build-job.active')
+ expect(page).to have_selector('[data-testid="active-job"]')
end
end
@@ -255,7 +255,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
end
it 'renders escaped tooltip name' do
- page.find('.active.build-job a').hover
+ page.find('[data-testid="active-job"]').hover
expect(page).to have_content('<img src=x onerror=alert(document.domain)> - passed')
end
end
diff --git a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
index 0a373b0d51a..4a25e28a14e 100644
--- a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
+++ b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
@@ -33,14 +33,14 @@ RSpec.describe 'Issue prioritization' do
sign_in user
visit project_issues_path(project, sort: 'label_priority')
+ wait_for_requests
+
# Ensure we are indicating that issues are sorted by priority
- expect(page).to have_selector('.dropdown', text: 'Label priority')
+ expect(page).to have_button 'Label priority'
- page.within('.issues-holder') do
- issue_titles = all('.issues-list .issue-title-text').map(&:text)
+ issue_titles = all('.issues-list .issue-title-text').map(&:text)
- expect(issue_titles).to eq(%w(issue_4 issue_3 issue_5 issue_2 issue_1))
- end
+ expect(issue_titles).to eq(%w(issue_4 issue_3 issue_5 issue_2 issue_1))
end
end
@@ -72,15 +72,15 @@ RSpec.describe 'Issue prioritization' do
sign_in user
visit project_issues_path(project, sort: 'label_priority')
- expect(page).to have_selector('.dropdown', text: 'Label priority')
+ wait_for_requests
+
+ expect(page).to have_button 'Label priority'
- page.within('.issues-holder') do
- issue_titles = all('.issues-list .issue-title-text').map(&:text)
+ issue_titles = all('.issues-list .issue-title-text').map(&:text)
- expect(issue_titles[0..1]).to contain_exactly('issue_5', 'issue_8')
- expect(issue_titles[2..4]).to contain_exactly('issue_1', 'issue_3', 'issue_7')
- expect(issue_titles[5..-1]).to eq(%w(issue_2 issue_4 issue_6))
- end
+ expect(issue_titles[0..1]).to contain_exactly('issue_5', 'issue_8')
+ expect(issue_titles[2..4]).to contain_exactly('issue_1', 'issue_3', 'issue_7')
+ expect(issue_titles[5..-1]).to eq(%w(issue_2 issue_4 issue_6))
end
end
end
diff --git a/spec/features/projects/labels/user_sees_links_to_issuables_spec.rb b/spec/features/projects/labels/user_sees_links_to_issuables_spec.rb
index 11aa53fd963..6f98883a412 100644
--- a/spec/features/projects/labels/user_sees_links_to_issuables_spec.rb
+++ b/spec/features/projects/labels/user_sees_links_to_issuables_spec.rb
@@ -51,6 +51,7 @@ RSpec.describe 'Projects > Labels > User sees links to issuables' do
context 'with a group label' do
let_it_be(:group) { create(:group) }
+
let(:label) { create(:group_label, group: group, title: 'bug') }
context 'when merge requests and issues are enabled for the project' do
diff --git a/spec/features/projects/labels/user_views_labels_spec.rb b/spec/features/projects/labels/user_views_labels_spec.rb
index da8520ca8fb..7a6942b6259 100644
--- a/spec/features/projects/labels/user_views_labels_spec.rb
+++ b/spec/features/projects/labels/user_views_labels_spec.rb
@@ -5,6 +5,7 @@ require "spec_helper"
RSpec.describe "User views labels" do
let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:user) { create(:user) }
+
let(:label_titles) { %w[bug enhancement feature] }
let!(:prioritized_label) { create(:label, project: project, title: 'prioritized-label-name', priority: 1) }
diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/invite_group_spec.rb
index 83ba2533a73..4caf3e947c7 100644
--- a/spec/features/projects/members/invite_group_spec.rb
+++ b/spec/features/projects/members/invite_group_spec.rb
@@ -6,25 +6,48 @@ RSpec.describe 'Project > Members > Invite group', :js do
include Select2Helper
include ActionView::Helpers::DateHelper
include Spec::Support::Helpers::Features::MembersHelpers
+ include Spec::Support::Helpers::Features::InviteMembersModalHelper
let(:maintainer) { create(:user) }
- before do
- stub_feature_flags(invite_members_group_modal: false)
+ using RSpec::Parameterized::TableSyntax
+
+ where(:invite_members_group_modal_enabled, :expected_invite_group_selector) do
+ true | 'button[data-qa-selector="invite_a_group_button"]'
+ false | '#invite-group-tab'
+ end
+
+ with_them do
+ before do
+ stub_feature_flags(invite_members_group_modal: invite_members_group_modal_enabled)
+ end
+
+ it 'displays either the invite group button or the form with tabs based on the feature flag' do
+ project = create(:project, namespace: create(:group))
+
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
+
+ visit project_project_members_path(project)
+
+ expect(page).to have_selector(expected_invite_group_selector)
+ end
end
describe 'Share with group lock' do
+ let(:invite_group_selector) { 'button[data-qa-selector="invite_a_group_button"]' }
+
shared_examples 'the project can be shared with groups' do
- it 'the "Invite group" tab exists' do
+ it 'the "Invite a group" button exists' do
visit project_project_members_path(project)
- expect(page).to have_selector('#invite-group-tab')
+ expect(page).to have_selector(invite_group_selector)
end
end
shared_examples 'the project cannot be shared with groups' do
- it 'the "Invite group" tab does not exist' do
+ it 'the "Invite a group" button does not exist' do
visit project_project_members_path(project)
- expect(page).not_to have_selector('#invite-group-tab')
+ expect(page).not_to have_selector(invite_group_selector)
end
end
@@ -41,7 +64,9 @@ 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
+ it 'the project can be shared with another group when the feature flag invite_members_group_modal is disabled' do
+ stub_feature_flags(invite_members_group_modal: false)
+
visit project_project_members_path(project)
expect(page).not_to have_link 'Groups'
@@ -56,6 +81,22 @@ RSpec.describe 'Project > Members > Invite group', :js do
expect(members_table).to have_content(group_to_share_with.name)
end
+
+ it 'the project can be shared with another group when the feature flag invite_members_group_modal is enabled' do
+ stub_feature_flags(invite_members_group_modal: true)
+
+ visit project_project_members_path(project)
+
+ expect(page).not_to have_link 'Groups'
+
+ invite_group(group_to_share_with.name)
+
+ visit project_project_members_path(project)
+
+ click_link 'Groups'
+
+ expect(members_table).to have_content(group_to_share_with.name)
+ end
end
context 'when the group has "Share with group lock" enabled' do
@@ -127,13 +168,7 @@ RSpec.describe 'Project > Members > Invite group', :js do
visit project_project_members_path(project)
- click_on 'invite-group-tab'
-
- select2 group.id, from: '#link_group_id'
-
- fill_in 'expires_at_groups', with: 5.days.from_now.strftime('%Y-%m-%d')
- click_on 'invite-group-tab'
- find('.btn-confirm').click
+ invite_group(group.name, role: 'Guest', expires_at: 5.days.from_now)
end
it 'the group link shows the expiration time with a warning class' do
@@ -149,29 +184,23 @@ RSpec.describe 'Project > Members > Invite group', :js do
context 'with multiple groups to choose from' do
let(:project) { create(:project) }
- before do
+ it 'includes multiple groups' do
project.add_maintainer(maintainer)
sign_in(maintainer)
- create(:group).add_owner(maintainer)
- create(:group).add_owner(maintainer)
+ group1 = create(:group)
+ group1.add_owner(maintainer)
+ group2 = create(:group)
+ group2.add_owner(maintainer)
visit project_project_members_path(project)
- click_link 'Invite group'
+ click_on 'Invite a group'
+ click_on 'Select a group'
+ wait_for_requests
- find('.ajax-groups-select.select2-container')
-
- execute_script 'GROUP_SELECT_PER_PAGE = 1;'
- open_select2 '#link_group_id'
- end
-
- it 'infinitely scrolls' do
- expect(find('.select2-drop .select2-results')).to have_selector('.select2-result', count: 1)
-
- scroll_select2_to_bottom('.select2-drop .select2-results:visible')
-
- expect(find('.select2-drop .select2-results')).to have_selector('.select2-result', count: 2)
+ expect(page).to have_button(group1.name)
+ expect(page).to have_button(group2.name)
end
end
@@ -188,16 +217,19 @@ RSpec.describe 'Project > Members > Invite group', :js do
group_to_share_with.add_maintainer(maintainer)
end
- it 'the groups dropdown does not show ancestors' do
+ # This behavior should be changed to exclude the ancestor and project
+ # group from the options once issue is fixed for the modal:
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/329835
+ it 'the groups dropdown does show ancestors and the project group' do
visit project_project_members_path(project)
- click_on 'invite-group-tab'
- click_link 'Search for a group'
+ click_on 'Invite a group'
+ click_on 'Select a group'
+ wait_for_requests
- page.within '.select2-drop' do
- expect(page).to have_content(group_to_share_with.name)
- expect(page).not_to have_content(group.name)
- end
+ expect(page).to have_button(group_to_share_with.name)
+ expect(page).to have_button(group.name)
+ expect(page).to have_button(nested_group.name)
end
end
end
diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb
index 384b8ae9929..f1fc579bb8a 100644
--- a/spec/features/projects/members/list_spec.rb
+++ b/spec/features/projects/members/list_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
RSpec.describe 'Project members list', :js do
- include Select2Helper
include Spec::Support::Helpers::Features::MembersHelpers
+ include Spec::Support::Helpers::Features::InviteMembersModalHelper
let(:user1) { create(:user, name: 'John Doe') }
let(:user2) { create(:user, name: 'Mary Jane') }
@@ -12,8 +12,6 @@ RSpec.describe 'Project members list', :js do
let(:project) { create(:project, :internal, namespace: group) }
before do
- stub_feature_flags(invite_members_group_modal: true)
-
sign_in(user1)
group.add_owner(user1)
end
@@ -52,7 +50,7 @@ RSpec.describe 'Project members list', :js do
it 'add user to project' do
visit_members_page
- add_user(user2.name, 'Reporter')
+ invite_member(user2.name, role: 'Reporter')
page.within find_member_row(user2) do
expect(page).to have_button('Reporter')
@@ -100,7 +98,7 @@ RSpec.describe 'Project members list', :js do
it 'invite user to project' do
visit_members_page
- add_user('test@example.com', 'Reporter')
+ invite_member('test@example.com', role: 'Reporter')
click_link 'Invited'
@@ -171,25 +169,6 @@ RSpec.describe 'Project members list', :js do
private
- def add_user(id, role)
- click_on 'Invite members'
-
- page.within '#invite-members-modal' do
- fill_in 'Select members or type email addresses', with: id
-
- wait_for_requests
- click_button id
-
- click_button 'Guest'
- wait_for_requests
- click_button role
-
- click_button 'Invite'
- end
-
- page.refresh
- end
-
def visit_members_page
visit project_project_members_path(project)
end
diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
index d22097a2f6f..c1b14cf60e7 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
@@ -3,12 +3,13 @@
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
+ include Spec::Support::Helpers::Features::InviteMembersModalHelper
let_it_be(:maintainer) { create(:user) }
let_it_be(:project) { create(:project) }
+
let(:new_member) { create(:user) }
before do
@@ -19,18 +20,9 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date
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: 5.days.from_now.to_date
- find_field('expires_at').native.send_keys :enter
-
- click_on 'Invite'
- end
+ invite_member(new_member.name, role: 'Guest', expires_at: 5.days.from_now.to_date)
page.within find_member_row(new_member) do
expect(page).to have_content(/in \d days/)
diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb
index 93bbabcc3f8..335ae6794b7 100644
--- a/spec/features/projects/merge_request_button_spec.rb
+++ b/spec/features/projects/merge_request_button_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe 'Merge Request button' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public, :repository) }
+
let(:forked_project) { fork_project(project, user, repository: true) }
shared_examples 'Merge request button only shown when allowed' do
diff --git a/spec/features/projects/milestones/gfm_autocomplete_spec.rb b/spec/features/projects/milestones/gfm_autocomplete_spec.rb
new file mode 100644
index 00000000000..547a5d11dec
--- /dev/null
+++ b/spec/features/projects/milestones/gfm_autocomplete_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'GFM autocomplete', :js do
+ let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
+ let_it_be(:group) { create(:group, name: 'Ancestor') }
+ let_it_be(:project) { create(:project, :repository, group: group) }
+ let_it_be(:issue) { create(:issue, project: project, assignees: [user], title: 'My special issue') }
+ let_it_be(:label) { create(:label, project: project, title: 'special+') }
+ let_it_be(:milestone) { create(:milestone, resource_parent: project, title: "project milestone") }
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
+
+ shared_examples 'displays autocomplete menu for all entities' do
+ it 'autocompletes all available entities' do
+ fill_in 'Description', with: User.reference_prefix
+ wait_for_requests
+ expect(find_autocomplete_menu).to be_visible
+ expect_autocomplete_entry(user.name)
+
+ fill_in 'Description', with: Label.reference_prefix
+ wait_for_requests
+ expect(find_autocomplete_menu).to be_visible
+ expect_autocomplete_entry(label.title)
+
+ fill_in 'Description', with: Milestone.reference_prefix
+ wait_for_requests
+ expect(find_autocomplete_menu).to be_visible
+ expect_autocomplete_entry(milestone.title)
+
+ fill_in 'Description', with: Issue.reference_prefix
+ wait_for_requests
+ expect(find_autocomplete_menu).to be_visible
+ expect_autocomplete_entry(issue.title)
+
+ fill_in 'Description', with: MergeRequest.reference_prefix
+ wait_for_requests
+ expect(find_autocomplete_menu).to be_visible
+ expect_autocomplete_entry(merge_request.title)
+ end
+ end
+
+ before_all do
+ group.add_maintainer(user)
+ end
+
+ describe 'new milestone page' do
+ before do
+ sign_in(user)
+ visit new_project_milestone_path(project)
+
+ wait_for_requests
+ end
+
+ it_behaves_like 'displays autocomplete menu for all entities'
+ end
+
+ describe 'update milestone page' do
+ before do
+ sign_in(user)
+ visit edit_project_milestone_path(project, milestone)
+
+ wait_for_requests
+ end
+
+ it_behaves_like 'displays autocomplete menu for all entities'
+ end
+
+ private
+
+ def find_autocomplete_menu
+ find('.atwho-view ul', visible: true)
+ end
+
+ def expect_autocomplete_entry(entry)
+ page.within('.atwho-container') do
+ expect(page).to have_content(entry)
+ end
+ end
+end
diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb
index 7dc3ee63669..ee5bf99fd75 100644
--- a/spec/features/projects/navbar_spec.rb
+++ b/spec/features/projects/navbar_spec.rb
@@ -8,62 +8,168 @@ RSpec.describe 'Project navbar' do
include_context 'project navbar structure'
- let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
- before do
- insert_package_nav(_('Operations'))
- insert_infrastructure_registry_nav
- stub_config(registry: { enabled: false })
+ let(:user) { project.owner }
- project.add_maintainer(user)
+ before do
sign_in(user)
end
- it_behaves_like 'verified navigation bar' do
+ context 'when sidebar refactor feature flag is disabled' do
before do
- visit project_path(project)
+ stub_feature_flags(sidebar_refactor: false)
+ insert_package_nav(_('Operations'))
+ insert_infrastructure_registry_nav
+
+ insert_after_sub_nav_item(
+ _('Boards'),
+ within: _('Issues'),
+ new_sub_nav_item_name: _('Labels')
+ )
+
+ insert_after_nav_item(
+ _('Snippets'),
+ new_nav_item: {
+ nav_item: _('Members'),
+ nav_sub_items: []
+ }
+ )
+
+ stub_config(registry: { enabled: false })
end
- end
- context 'when value stream is available' do
- before do
- visit project_path(project)
+ it_behaves_like 'verified navigation bar' do
+ before do
+ visit project_path(project)
+ end
end
- it 'redirects to value stream when Analytics item is clicked' do
- page.within('.sidebar-top-level-items') do
- find('[data-qa-selector=analytics_anchor]').click
+ context 'when value stream is available' do
+ before do
+ visit project_path(project)
end
- wait_for_requests
+ it 'redirects to value stream when Analytics item is clicked' do
+ page.within('.sidebar-top-level-items') do
+ find('.shortcuts-analytics').click
+ end
+
+ wait_for_requests
- expect(page).to have_current_path(project_cycle_analytics_path(project))
+ expect(page).to have_current_path(project_cycle_analytics_path(project))
+ end
end
- end
- context 'when pages are available' do
- before do
- stub_config(pages: { enabled: true })
+ context 'when pages are available' do
+ before do
+ stub_config(pages: { enabled: true })
- insert_after_sub_nav_item(
- _('Operations'),
- within: _('Settings'),
- new_sub_nav_item_name: _('Pages')
- )
+ insert_after_sub_nav_item(
+ _('Operations'),
+ within: _('Settings'),
+ new_sub_nav_item_name: _('Pages')
+ )
- visit project_path(project)
+ visit project_path(project)
+ end
+
+ it_behaves_like 'verified navigation bar'
end
- it_behaves_like 'verified navigation bar'
+ context 'when container registry is available' do
+ before do
+ stub_config(registry: { enabled: true })
+
+ insert_container_nav
+
+ visit project_path(project)
+ end
+
+ it_behaves_like 'verified navigation bar'
+ end
end
- context 'when container registry is available' do
+ context 'when sidebar refactor feature flag is enabled' do
+ let(:monitor_nav_item) do
+ {
+ nav_item: _('Monitor'),
+ nav_sub_items: monitor_menu_items
+ }
+ end
+
+ let(:monitor_menu_items) do
+ [
+ _('Metrics'),
+ _('Logs'),
+ _('Tracing'),
+ _('Error Tracking'),
+ _('Alerts'),
+ _('Incidents'),
+ _('Product Analytics')
+ ]
+ end
+
+ let(:project_information_nav_item) do
+ {
+ nav_item: _('Project information'),
+ nav_sub_items: [
+ _('Activity'),
+ _('Labels'),
+ _('Members')
+ ]
+ }
+ end
+
+ let(:settings_menu_items) do
+ [
+ _('General'),
+ _('Integrations'),
+ _('Webhooks'),
+ _('Access Tokens'),
+ _('Repository'),
+ _('CI/CD'),
+ _('Monitor')
+ ]
+ end
+
before do
+ stub_feature_flags(sidebar_refactor: true)
stub_config(registry: { enabled: true })
-
+ insert_package_nav(_('Monitor'))
+ insert_infrastructure_registry_nav
insert_container_nav
+ insert_after_sub_nav_item(
+ _('Monitor'),
+ within: _('Settings'),
+ new_sub_nav_item_name: _('Packages & Registries')
+ )
+
+ insert_after_nav_item(
+ _('Monitor'),
+ new_nav_item: {
+ nav_item: _('Infrastructure'),
+ nav_sub_items: [
+ _('Kubernetes clusters'),
+ _('Serverless platform'),
+ _('Terraform')
+ ]
+ }
+ )
+
+ insert_after_nav_item(
+ _('Security & Compliance'),
+ new_nav_item: {
+ nav_item: _('Deployments'),
+ nav_sub_items: [
+ _('Feature Flags'),
+ _('Environments'),
+ _('Releases')
+ ]
+ }
+ )
+
visit project_path(project)
end
diff --git a/spec/features/projects/new_project_from_template_spec.rb b/spec/features/projects/new_project_from_template_spec.rb
new file mode 100644
index 00000000000..1c8647d859a
--- /dev/null
+++ b/spec/features/projects/new_project_from_template_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'New project from template', :js do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+
+ visit new_project_path
+ end
+
+ context 'create from template' do
+ before do
+ page.find('a[href="#create_from_template"]').click
+ wait_for_requests
+ end
+
+ it 'shows template tabs' do
+ page.within('#create-from-template-pane') do
+ expect(page).to have_link('Built-in', href: '#built-in')
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index 7119039d5ff..a1523f9eb08 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -5,50 +5,48 @@ require 'spec_helper'
RSpec.describe 'New project', :js do
include Select2Helper
- context 'as a user' do
- let(:user) { create(:user) }
+ shared_examples 'combined_menu: feature flag examples' do
+ context 'as a user' do
+ let(:user) { create(:user) }
- before do
- sign_in(user)
- end
-
- context 'new repo experiment', :experiment do
- it 'when in control renders "project"' do
- stub_experiments(new_repo: :control)
+ before do
+ sign_in(user)
+ end
- visit new_project_path
+ context 'new repo experiment', :experiment do
+ it 'when in control renders "project"' do
+ stub_experiments(new_repo: :control)
- find('li.header-new.dropdown').click
+ visit new_project_path
- page.within('li.header-new.dropdown') do
- expect(page).to have_selector('a', text: 'New project')
- expect(page).to have_no_selector('a', text: 'New project/repository')
- end
+ find('li.header-new.dropdown').click
- expect(page).to have_selector('.blank-state-title', text: 'Create blank project')
- expect(page).to have_no_selector('.blank-state-title', text: 'Create blank project/repository')
- end
+ page.within('li.header-new.dropdown') do
+ expect(page).to have_selector('a', text: 'New project')
+ expect(page).to have_no_selector('a', text: 'New project/repository')
+ end
- it 'when in candidate renders "project/repository"' do
- stub_experiments(new_repo: :candidate)
+ expect(page).to have_selector('h3', text: 'Create blank project')
+ expect(page).to have_no_selector('h3', text: 'Create blank project/repository')
+ end
- visit new_project_path
+ it 'when in candidate renders "project/repository"' do
+ stub_experiments(new_repo: :candidate)
- find('li.header-new.dropdown').click
+ visit new_project_path
- page.within('li.header-new.dropdown') do
- expect(page).to have_selector('a', text: 'New project/repository')
- end
+ find('li.header-new.dropdown').click
- expect(page).to have_selector('.blank-state-title', text: 'Create blank project/repository')
- end
+ page.within('li.header-new.dropdown') do
+ expect(page).to have_selector('a', text: 'New project/repository')
+ end
- context 'with combined_menu feature disabled' do
- before do
- stub_feature_flags(combined_menu: false)
+ expect(page).to have_selector('h3', text: 'Create blank project/repository')
end
it 'when in control it renders "project" in the new projects dropdown' do
+ pending_on_combined_menu_flag
+
stub_experiments(new_repo: :control)
visit new_project_path
@@ -64,6 +62,8 @@ RSpec.describe 'New project', :js do
end
it 'when in candidate it renders "project/repository" in the new projects dropdown' do
+ pending_on_combined_menu_flag
+
stub_experiments(new_repo: :candidate)
visit new_project_path
@@ -76,337 +76,373 @@ RSpec.describe 'New project', :js do
end
end
end
- end
- it 'shows a message if multiple levels are restricted' do
- Gitlab::CurrentSettings.update!(
- restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL]
- )
+ it 'shows a message if multiple levels are restricted' do
+ Gitlab::CurrentSettings.update!(
+ restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL]
+ )
- visit new_project_path
- find('[data-qa-selector="blank_project_link"]').click
-
- expect(page).to have_content 'Other visibility settings have been disabled by the administrator.'
- end
+ visit new_project_path
+ find('[data-qa-selector="blank_project_link"]').click
- it 'shows a message if all levels are restricted' do
- Gitlab::CurrentSettings.update!(
- restricted_visibility_levels: Gitlab::VisibilityLevel.values
- )
+ expect(page).to have_content 'Other visibility settings have been disabled by the administrator.'
+ end
- visit new_project_path
- find('[data-qa-selector="blank_project_link"]').click
+ it 'shows a message if all levels are restricted' do
+ Gitlab::CurrentSettings.update!(
+ restricted_visibility_levels: Gitlab::VisibilityLevel.values
+ )
- expect(page).to have_content 'Visibility settings have been disabled by the administrator.'
- end
- end
-
- context 'as an admin' do
- let(:user) { create(:admin) }
+ visit new_project_path
+ find('[data-qa-selector="blank_project_link"]').click
- before do
- sign_in(user)
+ expect(page).to have_content 'Visibility settings have been disabled by the administrator.'
+ end
end
- it 'shows "New project" page', :js do
- visit new_project_path
- find('[data-qa-selector="blank_project_link"]').click
-
- expect(page).to have_content('Project name')
- expect(page).to have_content('Project URL')
- expect(page).to have_content('Project slug')
-
- click_link('New project')
- find('[data-qa-selector="import_project_link"]').click
+ context 'as an admin' do
+ let(:user) { create(:admin) }
- expect(page).to have_link('GitHub')
- expect(page).to have_link('Bitbucket')
- expect(page).to have_link('GitLab.com')
- expect(page).to have_button('Repo by URL')
- expect(page).to have_link('GitLab export')
- end
-
- describe 'manifest import option' do
before do
+ sign_in(user)
+ end
+
+ it 'shows "New project" page', :js do
visit new_project_path
+ find('[data-qa-selector="blank_project_link"]').click
- find('[data-qa-selector="import_project_link"]').click
- end
+ expect(page).to have_content('Project name')
+ expect(page).to have_content('Project URL')
+ expect(page).to have_content('Project slug')
- it { expect(page).to have_link('Manifest file') }
- end
+ click_link('New project')
+ find('[data-qa-selector="import_project_link"]').click
- context 'Visibility level selector', :js do
- Gitlab::VisibilityLevel.options.each do |key, level|
- it "sets selector to #{key}" do
- stub_application_setting(default_project_visibility: level)
+ expect(page).to have_link('GitHub')
+ expect(page).to have_link('Bitbucket')
+ expect(page).to have_link('GitLab.com')
+ expect(page).to have_button('Repo by URL')
+ expect(page).to have_link('GitLab export')
+ end
+ describe 'manifest import option' do
+ before do
visit new_project_path
- find('[data-qa-selector="blank_project_link"]').click
- page.within('#blank-project-pane') do
- expect(find_field("project_visibility_level_#{level}")).to be_checked
- end
- end
- it "saves visibility level #{level} on validation error" do
- visit new_project_path
- find('[data-qa-selector="blank_project_link"]').click
+ find('[data-qa-selector="import_project_link"]').click
+ end
- choose(key)
- click_button('Create project')
- page.within('#blank-project-pane') do
- expect(find_field("project_visibility_level_#{level}")).to be_checked
- end
+ it 'has Manifest file' do
+ expect(page).to have_link('Manifest file')
end
end
- context 'when group visibility is private but default is internal' do
- let_it_be(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
+ context 'Visibility level selector', :js do
+ Gitlab::VisibilityLevel.options.each do |key, level|
+ it "sets selector to #{key}" do
+ stub_application_setting(default_project_visibility: level)
- before do
- stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL)
- end
+ visit new_project_path
+ find('[data-qa-selector="blank_project_link"]').click
+ page.within('#blank-project-pane') do
+ expect(find_field("project_visibility_level_#{level}")).to be_checked
+ end
+ end
- context 'when admin mode is enabled', :enable_admin_mode do
- it 'has private selected' do
- visit new_project_path(namespace_id: group.id)
+ it "saves visibility level #{level} on validation error" do
+ visit new_project_path
find('[data-qa-selector="blank_project_link"]').click
+ choose(key)
+ click_button('Create project')
page.within('#blank-project-pane') do
- expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked
+ expect(find_field("project_visibility_level_#{level}")).to be_checked
end
end
end
- context 'when admin mode is disabled' do
- it 'is not allowed' do
- visit new_project_path(namespace_id: group.id)
+ context 'when group visibility is private but default is internal' do
+ let_it_be(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
- expect(page).to have_content('Not Found')
+ before do
+ stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL)
end
- end
- end
- context 'when group visibility is public but user requests private' do
- let_it_be(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it 'has private selected' do
+ visit new_project_path(namespace_id: group.id)
+ find('[data-qa-selector="blank_project_link"]').click
- before do
- stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL)
- end
+ page.within('#blank-project-pane') do
+ expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked
+ end
+ end
+ end
- context 'when admin mode is enabled', :enable_admin_mode do
- it 'has private selected' do
- visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
- find('[data-qa-selector="blank_project_link"]').click
+ context 'when admin mode is disabled' do
+ it 'is not allowed' do
+ visit new_project_path(namespace_id: group.id)
- page.within('#blank-project-pane') do
- expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked
+ expect(page).to have_content('Not Found')
end
end
end
- context 'when admin mode is disabled' do
- it 'is not allowed' do
- visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
+ context 'when group visibility is public but user requests private' do
+ let_it_be(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
- expect(page).to have_content('Not Found')
+ before do
+ stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL)
end
- end
- end
- end
- context 'Readme selector' do
- it 'shows the initialize with Readme checkbox on "Blank project" tab' do
- visit new_project_path
- find('[data-qa-selector="blank_project_link"]').click
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it 'has private selected' do
+ visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
+ find('[data-qa-selector="blank_project_link"]').click
- expect(page).to have_css('input#project_initialize_with_readme')
- expect(page).to have_content('Initialize repository with a README')
- end
+ page.within('#blank-project-pane') do
+ expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked
+ end
+ end
+ end
- it 'does not show the initialize with Readme checkbox on "Create from template" tab' do
- visit new_project_path
- find('[data-qa-selector="create_from_template_link"]').click
- first('.choose-template').click
+ context 'when admin mode is disabled' do
+ it 'is not allowed' do
+ visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
- page.within '.project-fields-form' do
- expect(page).not_to have_css('input#project_initialize_with_readme')
- expect(page).not_to have_content('Initialize repository with a README')
+ expect(page).to have_content('Not Found')
+ end
+ end
end
end
- it 'does not show the initialize with Readme checkbox on "Import project" tab' do
- visit new_project_path
- find('[data-qa-selector="import_project_link"]').click
- first('.js-import-git-toggle-button').click
+ context 'Readme selector' do
+ it 'shows the initialize with Readme checkbox on "Blank project" tab' do
+ visit new_project_path
+ find('[data-qa-selector="blank_project_link"]').click
- page.within '.toggle-import-form' do
- expect(page).not_to have_css('input#project_initialize_with_readme')
- expect(page).not_to have_content('Initialize repository with a README')
+ expect(page).to have_css('input#project_initialize_with_readme')
+ expect(page).to have_content('Initialize repository with a README')
end
- end
- end
- context 'Namespace selector' do
- context 'with user namespace' do
- before do
+ it 'does not show the initialize with Readme checkbox on "Create from template" tab' do
visit new_project_path
- find('[data-qa-selector="blank_project_link"]').click
+ find('[data-qa-selector="create_from_template_link"]').click
+ first('.choose-template').click
+
+ page.within '.project-fields-form' do
+ expect(page).not_to have_css('input#project_initialize_with_readme')
+ expect(page).not_to have_content('Initialize repository with a README')
+ end
end
- it 'selects the user namespace' do
- page.within('#blank-project-pane') do
- expect(page).to have_select('project[namespace_id]', visible: false, selected: user.username)
+ it 'does not show the initialize with Readme checkbox on "Import project" tab' do
+ visit new_project_path
+ find('[data-qa-selector="import_project_link"]').click
+ first('.js-import-git-toggle-button').click
+
+ page.within '#import-project-pane' do
+ expect(page).not_to have_css('input#project_initialize_with_readme')
+ expect(page).not_to have_content('Initialize repository with a README')
end
end
end
- context 'with group namespace' do
- let(:group) { create(:group, :private) }
+ context 'Namespace selector' do
+ context 'with user namespace' do
+ before do
+ visit new_project_path
+ find('[data-qa-selector="blank_project_link"]').click
+ end
- before do
- group.add_owner(user)
- visit new_project_path(namespace_id: group.id)
- find('[data-qa-selector="blank_project_link"]').click
+ it 'selects the user namespace' do
+ page.within('#blank-project-pane') do
+ expect(page).to have_select('project[namespace_id]', visible: false, selected: user.username)
+ end
+ end
end
- it 'selects the group namespace' do
- page.within('#blank-project-pane') do
- expect(page).to have_select('project[namespace_id]', visible: false, selected: group.name)
+ context 'with group namespace' do
+ let(:group) { create(:group, :private) }
+
+ before do
+ group.add_owner(user)
+ visit new_project_path(namespace_id: group.id)
+ find('[data-qa-selector="blank_project_link"]').click
+ end
+
+ it 'selects the group namespace' do
+ page.within('#blank-project-pane') do
+ expect(page).to have_select('project[namespace_id]', visible: false, selected: group.name)
+ end
end
end
- end
- context 'with subgroup namespace' do
- let(:group) { create(:group) }
- let(:subgroup) { create(:group, parent: group) }
+ context 'with subgroup namespace' do
+ let(:group) { create(:group) }
+ let(:subgroup) { create(:group, parent: group) }
- before do
- group.add_maintainer(user)
- visit new_project_path(namespace_id: subgroup.id)
- find('[data-qa-selector="blank_project_link"]').click
- end
+ before do
+ group.add_maintainer(user)
+ visit new_project_path(namespace_id: subgroup.id)
+ find('[data-qa-selector="blank_project_link"]').click
+ end
- it 'selects the group namespace' do
- page.within('#blank-project-pane') do
- expect(page).to have_select('project[namespace_id]', visible: false, selected: subgroup.full_path)
+ it 'selects the group namespace' do
+ page.within('#blank-project-pane') do
+ expect(page).to have_select('project[namespace_id]', visible: false, selected: subgroup.full_path)
+ end
end
end
- end
- context 'when changing namespaces dynamically', :js do
- let(:public_group) { create(:group, :public) }
- let(:internal_group) { create(:group, :internal) }
- let(:private_group) { create(:group, :private) }
+ context 'when changing namespaces dynamically', :js do
+ let(:public_group) { create(:group, :public) }
+ let(:internal_group) { create(:group, :internal) }
+ let(:private_group) { create(:group, :private) }
- before do
- public_group.add_owner(user)
- internal_group.add_owner(user)
- private_group.add_owner(user)
- visit new_project_path(namespace_id: public_group.id)
- find('[data-qa-selector="blank_project_link"]').click
- end
+ before do
+ public_group.add_owner(user)
+ internal_group.add_owner(user)
+ private_group.add_owner(user)
+ visit new_project_path(namespace_id: public_group.id)
+ find('[data-qa-selector="blank_project_link"]').click
+ end
- it 'enables the correct visibility options' do
- select2(user.namespace_id, from: '#project_namespace_id')
- expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
- expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled
- expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).not_to be_disabled
-
- select2(public_group.id, from: '#project_namespace_id')
- expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
- expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled
- expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).not_to be_disabled
-
- select2(internal_group.id, from: '#project_namespace_id')
- expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
- expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled
- expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).to be_disabled
-
- select2(private_group.id, from: '#project_namespace_id')
- expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
- expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).to be_disabled
- expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).to be_disabled
+ it 'enables the correct visibility options' do
+ select2(user.namespace_id, from: '#project_namespace_id')
+ expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
+ expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled
+ expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).not_to be_disabled
+
+ select2(public_group.id, from: '#project_namespace_id')
+ expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
+ expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled
+ expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).not_to be_disabled
+
+ select2(internal_group.id, from: '#project_namespace_id')
+ expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
+ expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled
+ expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).to be_disabled
+
+ select2(private_group.id, from: '#project_namespace_id')
+ expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
+ expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).to be_disabled
+ expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).to be_disabled
+ end
end
end
- end
- context 'Import project options', :js do
- before do
- visit new_project_path
- find('[data-qa-selector="import_project_link"]').click
- end
-
- context 'from git repository url, "Repo by URL"' do
+ context 'Import project options', :js do
before do
- first('.js-import-git-toggle-button').click
+ visit new_project_path
+ find('[data-qa-selector="import_project_link"]').click
end
- it 'does not autocomplete sensitive git repo URL' do
- autocomplete = find('#project_import_url')['autocomplete']
+ context 'from git repository url, "Repo by URL"' do
+ before do
+ first('.js-import-git-toggle-button').click
+ end
- expect(autocomplete).to eq('off')
- end
+ it 'does not autocomplete sensitive git repo URL' do
+ autocomplete = find('#project_import_url')['autocomplete']
- it 'shows import instructions' do
- git_import_instructions = first('.js-toggle-content')
+ expect(autocomplete).to eq('off')
+ end
- expect(git_import_instructions).to be_visible
- expect(git_import_instructions).to have_content 'Git repository URL'
- end
+ it 'shows import instructions' do
+ git_import_instructions = first('.js-toggle-content')
- it 'keeps "Import project" tab open after form validation error' do
- collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace)
+ expect(git_import_instructions).to be_visible
+ expect(git_import_instructions).to have_content 'Git repository URL'
+ end
- fill_in 'project_import_url', with: collision_project.http_url_to_repo
- fill_in 'project_name', with: collision_project.name
+ it 'reports error if repo URL does not end with .git' do
+ fill_in 'project_import_url', with: 'http://foo/bar'
+ fill_in 'project_name', with: 'import-project-without-git-suffix'
+ fill_in 'project_path', with: 'import-project-without-git-suffix'
- click_on 'Create project'
+ click_button 'Create project'
- expect(page).to have_css('#import-project-pane.active')
- expect(page).not_to have_css('.toggle-import-form.hide')
- end
- end
+ expect(page).to have_text('Please provide a valid URL ending with .git')
+ end
- context 'from GitHub' do
- before do
- first('.js-import-github').click
- end
+ it 'keeps "Import project" tab open after form validation error' do
+ collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace)
+
+ fill_in 'project_import_url', with: collision_project.http_url_to_repo
+ fill_in 'project_name', with: collision_project.name
+
+ click_on 'Create project'
- it 'shows import instructions' do
- expect(page).to have_content('Authenticate with GitHub')
- expect(current_path).to eq new_import_github_path
+ expect(page).to have_css('#import-project-pane.active')
+ expect(page).not_to have_css('.toggle-import-form.hide')
+ end
end
- end
- context 'from manifest file' do
- before do
- first('.import_manifest').click
+ context 'from GitHub' do
+ before do
+ first('.js-import-github').click
+ end
+
+ it 'shows import instructions' do
+ expect(page).to have_content('Authenticate with GitHub')
+ expect(current_path).to eq new_import_github_path
+ end
end
- it 'shows import instructions' do
- expect(page).to have_content('Manifest file import')
- expect(current_path).to eq new_import_manifest_path
+ context 'from manifest file' do
+ before do
+ first('.import_manifest').click
+ end
+
+ it 'shows import instructions' do
+ expect(page).to have_content('Manifest file import')
+ expect(current_path).to eq new_import_manifest_path
+ end
end
end
- end
- context 'Namespace selector' do
- context 'with group with DEVELOPER_MAINTAINER_PROJECT_ACCESS project_creation_level' do
- let(:group) { create(:group, project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) }
+ context 'Namespace selector' do
+ context 'with group with DEVELOPER_MAINTAINER_PROJECT_ACCESS project_creation_level' do
+ let(:group) { create(:group, project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) }
- before do
- group.add_developer(user)
- visit new_project_path(namespace_id: group.id)
- find('[data-qa-selector="blank_project_link"]').click
- end
+ before do
+ group.add_developer(user)
+ visit new_project_path(namespace_id: group.id)
+ find('[data-qa-selector="blank_project_link"]').click
+ end
- it 'selects the group namespace' do
- page.within('#blank-project-pane') do
- expect(page).to have_select('project[namespace_id]', visible: false, selected: group.full_path)
+ it 'selects the group namespace' do
+ page.within('#blank-project-pane') do
+ expect(page).to have_select('project[namespace_id]', visible: false, selected: group.full_path)
+ end
end
end
end
end
end
+
+ context 'with combined_menu: feature flag on' do
+ let(:needs_rewrite_for_combined_menu_flag_on) { true }
+
+ before do
+ stub_feature_flags(combined_menu: true)
+ end
+
+ it_behaves_like 'combined_menu: feature flag examples'
+ end
+
+ context 'with combined_menu feature flag off' do
+ let(:needs_rewrite_for_combined_menu_flag_on) { false }
+
+ before do
+ stub_feature_flags(combined_menu: false)
+ end
+
+ it_behaves_like 'combined_menu: feature flag examples'
+ end
+
+ def pending_on_combined_menu_flag
+ pending 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587' if needs_rewrite_for_combined_menu_flag_on
+ end
end
diff --git a/spec/features/projects/pages/user_adds_domain_spec.rb b/spec/features/projects/pages/user_adds_domain_spec.rb
index 24c9edb79e5..de9effe3dc7 100644
--- a/spec/features/projects/pages/user_adds_domain_spec.rb
+++ b/spec/features/projects/pages/user_adds_domain_spec.rb
@@ -5,6 +5,7 @@ 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
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 4a0581bb5cf..70dc0bd04e8 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -739,6 +739,7 @@ RSpec.describe 'Pipeline', :js do
context 'when build requires resource', :sidekiq_inline do
let_it_be(:project) { create(:project, :repository) }
+
let(:pipeline) { create(:ci_pipeline, project: project) }
let(:resource_group) { create(:ci_resource_group, project: project) }
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index e375bc10dbf..f1672af1019 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -457,22 +457,8 @@ RSpec.describe 'Pipelines', :js do
visit_project_pipelines
end
- it 'has artifacts' do
- expect(page).to have_selector('.build-artifacts')
- end
-
- it 'has artifacts download dropdown' do
- find('.js-pipeline-dropdown-download').click
-
- expect(page).to have_link(with_artifacts.file_type)
- end
-
- it 'has download attribute on download links' do
- find('.js-pipeline-dropdown-download').click
- expect(page).to have_selector('a', text: 'Download')
- page.all('.build-artifacts a', text: 'Download').each do |link|
- expect(link[:download]).to eq ''
- end
+ it 'has artifacts dropdown' do
+ expect(page).to have_selector('[data-testid="pipeline-multi-actions-dropdown"]')
end
end
@@ -488,7 +474,7 @@ RSpec.describe 'Pipelines', :js do
visit_project_pipelines
end
- it { expect(page).not_to have_selector('.build-artifacts') }
+ it { expect(page).not_to have_selector('[data-testid="artifact-item"]') }
end
context 'without artifacts' do
@@ -503,7 +489,7 @@ RSpec.describe 'Pipelines', :js do
visit_project_pipelines
end
- it { expect(page).not_to have_selector('.build-artifacts') }
+ it { expect(page).not_to have_selector('[data-testid="artifact-item"]') }
end
context 'with trace artifact' do
@@ -514,7 +500,7 @@ RSpec.describe 'Pipelines', :js do
end
it 'does not show trace artifact as artifacts' do
- expect(page).not_to have_selector('.build-artifacts')
+ expect(page).not_to have_selector('[data-testid="artifact-item"]')
end
end
end
@@ -657,26 +643,28 @@ RSpec.describe 'Pipelines', :js do
let(:project) { create(:project, :repository) }
before do
- stub_feature_flags(new_pipeline_form: false)
visit new_project_pipeline_path(project)
end
context 'for valid commit', :js do
before do
click_button project.default_branch
+ wait_for_requests
- page.within '.dropdown-menu' do
- click_link 'master'
- end
+ find('p', text: 'master').click
+ wait_for_requests
end
- context 'with gitlab-ci.yml' do
+ context 'with gitlab-ci.yml', :js do
before do
stub_ci_pipeline_to_return_yaml_file
end
it 'creates a new pipeline' do
- expect { click_on 'Run pipeline' }
+ expect do
+ click_on 'Run pipeline'
+ wait_for_requests
+ end
.to change { Ci::Pipeline.count }.by(1)
expect(Ci::Pipeline.last).to be_web
@@ -684,12 +672,15 @@ RSpec.describe 'Pipelines', :js do
context 'when variables are specified' do
it 'creates a new pipeline with variables' do
- page.within '.ci-variable-row-body' do
- fill_in "Input variable key", with: "key_name"
- fill_in "Input variable value", with: "value"
+ page.within(find("[data-testid='ci-variable-row']")) do
+ find("[data-testid='pipeline-form-ci-variable-key']").set('key_name')
+ find("[data-testid='pipeline-form-ci-variable-value']").set('value')
end
- expect { click_on 'Run pipeline' }
+ expect do
+ click_on 'Run pipeline'
+ wait_for_requests
+ end
.to change { Ci::Pipeline.count }.by(1)
expect(Ci::Pipeline.last.variables.map { |var| var.slice(:key, :secret_value) })
@@ -701,19 +692,17 @@ RSpec.describe 'Pipelines', :js do
context 'without gitlab-ci.yml' do
before do
click_on 'Run pipeline'
+ wait_for_requests
end
it { expect(page).to have_content('Missing CI config file') }
it 'creates a pipeline after first request failed and a valid gitlab-ci.yml file is available when trying again' do
- click_button project.default_branch
-
stub_ci_pipeline_to_return_yaml_file
- page.within '.dropdown-menu' do
- click_link 'master'
+ expect do
+ click_on 'Run pipeline'
+ wait_for_requests
end
-
- expect { click_on 'Run pipeline' }
.to change { Ci::Pipeline.count }.by(1)
end
end
@@ -760,14 +749,13 @@ RSpec.describe 'Pipelines', :js do
let(:project) { create(:project, :repository) }
before do
- stub_feature_flags(new_pipeline_form: false)
visit new_project_pipeline_path(project)
end
describe 'new pipeline page' do
it 'has field to add a new pipeline' do
- expect(page).to have_selector('.js-branch-select')
- expect(find('.js-branch-select')).to have_content project.default_branch
+ expect(page).to have_selector('[data-testid="ref-select"]')
+ expect(find('[data-testid="ref-select"]')).to have_content project.default_branch
expect(page).to have_content('Run for')
end
end
@@ -776,10 +764,10 @@ RSpec.describe 'Pipelines', :js do
it 'shows filtered pipelines', :js do
click_button project.default_branch
- page.within '.dropdown-menu' do
- find('.dropdown-input-field').native.send_keys('fix')
+ page.within '[data-testid="ref-select"]' do
+ find('[data-testid="search-refs"]').native.send_keys('fix')
- page.within '.dropdown-content' do
+ page.within '.gl-new-dropdown-contents' do
expect(page).to have_content('fix')
end
end
diff --git a/spec/features/projects/product_analytics/events_spec.rb b/spec/features/projects/product_analytics/events_spec.rb
index 12f1c4d291a..05d12e12acb 100644
--- a/spec/features/projects/product_analytics/events_spec.rb
+++ b/spec/features/projects/product_analytics/events_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe 'Product Analytics > Events' do
let_it_be(:project) { create(:project_empty_repo) }
let_it_be(:user) { create(:user) }
+
let(:event) { create(:product_analytics_event, project: project) }
before do
diff --git a/spec/features/projects/releases/user_views_releases_spec.rb b/spec/features/projects/releases/user_views_releases_spec.rb
index aabbc8cea7b..d8a55fc7f3b 100644
--- a/spec/features/projects/releases/user_views_releases_spec.rb
+++ b/spec/features/projects/releases/user_views_releases_spec.rb
@@ -19,143 +19,129 @@ RSpec.describe 'User views releases', :js do
project.add_guest(guest)
end
- shared_examples 'releases page' do
- context('when the user is a maintainer') do
- before do
- sign_in(maintainer)
+ context('when the user is a maintainer') do
+ before do
+ sign_in(maintainer)
+
+ visit project_releases_path(project)
+ end
- visit project_releases_path(project)
+ it 'sees the release' do
+ page.within("##{release_v1.tag}") do
+ expect(page).to have_content(release_v1.name)
+ expect(page).to have_content(release_v1.tag)
+ expect(page).not_to have_content('Upcoming Release')
end
+ end
- it 'sees the release' do
- page.within("##{release_v1.tag}") do
- expect(page).to have_content(release_v1.name)
- expect(page).to have_content(release_v1.tag)
- expect(page).not_to have_content('Upcoming Release')
+ context 'when there is a link as an asset' do
+ let!(:release_link) { create(:release_link, release: release_v1, url: url ) }
+ let(:url) { "#{project.web_url}/-/jobs/1/artifacts/download" }
+ let(:direct_asset_link) { Gitlab::Routing.url_helpers.project_release_url(project, release_v1) << "/downloads#{release_link.filepath}" }
+
+ it 'sees the link' do
+ page.within("##{release_v1.tag} .js-assets-list") do
+ expect(page).to have_link release_link.name, href: direct_asset_link
+ expect(page).not_to have_css('[data-testid="external-link-indicator"]')
end
end
- context 'when there is a link as an asset' do
- let!(:release_link) { create(:release_link, release: release_v1, url: url ) }
+ context 'when there is a link redirect' do
+ let!(:release_link) { create(:release_link, release: release_v1, name: 'linux-amd64 binaries', filepath: '/binaries/linux-amd64', url: url) }
let(:url) { "#{project.web_url}/-/jobs/1/artifacts/download" }
- let(:direct_asset_link) { Gitlab::Routing.url_helpers.project_release_url(project, release_v1) << "/downloads#{release_link.filepath}" }
- it 'sees the link' do
+ it 'sees the link', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/329301' do
page.within("##{release_v1.tag} .js-assets-list") do
expect(page).to have_link release_link.name, href: direct_asset_link
expect(page).not_to have_css('[data-testid="external-link-indicator"]')
end
end
+ end
- context 'when there is a link redirect' do
- let!(:release_link) { create(:release_link, release: release_v1, name: 'linux-amd64 binaries', filepath: '/binaries/linux-amd64', url: url) }
- let(:url) { "#{project.web_url}/-/jobs/1/artifacts/download" }
-
- it 'sees the link' do
- page.within("##{release_v1.tag} .js-assets-list") do
- expect(page).to have_link release_link.name, href: direct_asset_link
- expect(page).not_to have_css('[data-testid="external-link-indicator"]')
- end
- end
- end
-
- context 'when url points to external resource' do
- let(:url) { 'http://google.com/download' }
+ context 'when url points to external resource' do
+ let(:url) { 'http://google.com/download' }
- it 'sees that the link is external resource' do
- page.within("##{release_v1.tag} .js-assets-list") do
- expect(page).to have_css('[data-testid="external-link-indicator"]')
- end
+ it 'sees that the link is external resource', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/329302' do
+ page.within("##{release_v1.tag} .js-assets-list") do
+ expect(page).to have_css('[data-testid="external-link-indicator"]')
end
end
end
+ end
- context 'with an upcoming release' do
- it 'sees the upcoming tag' do
- page.within("##{release_v3.tag}") do
- expect(page).to have_content('Upcoming Release')
- end
+ context 'with an upcoming release' do
+ it 'sees the upcoming tag' do
+ page.within("##{release_v3.tag}") do
+ expect(page).to have_content('Upcoming Release')
end
end
+ end
- context 'with a tag containing a slash' do
- it 'sees the release' do
- page.within("##{release_v2.tag.parameterize}") do
- expect(page).to have_content(release_v2.name)
- expect(page).to have_content(release_v2.tag)
- end
+ context 'with a tag containing a slash' do
+ it 'sees the release' do
+ page.within("##{release_v2.tag.parameterize}") do
+ expect(page).to have_content(release_v2.name)
+ expect(page).to have_content(release_v2.tag)
end
end
+ end
- context 'sorting' do
- def sort_page(by:, direction:)
- within '[data-testid="releases-sort"]' do
- find('.dropdown-toggle').click
-
- click_button(by, class: 'dropdown-item')
-
- find('.sorting-direction-button').click if direction == :ascending
- end
- end
-
- shared_examples 'releases sort order' do
- it "sorts the releases #{description}" do
- card_titles = page.all('.release-block .card-title', minimum: expected_releases.count)
-
- card_titles.each_with_index do |title, index|
- expect(title).to have_content(expected_releases[index].name)
- end
- end
- end
+ context 'sorting' do
+ def sort_page(by:, direction:)
+ within '[data-testid="releases-sort"]' do
+ find('.dropdown-toggle').click
- context "when the page is sorted by the default sort order" do
- let(:expected_releases) { [release_v3, release_v2, release_v1] }
+ click_button(by, class: 'dropdown-item')
- it_behaves_like 'releases sort order'
+ find('.sorting-direction-button').click if direction == :ascending
end
+ end
- context "when the page is sorted by created_at ascending " do
- let(:expected_releases) { [release_v2, release_v1, release_v3] }
+ shared_examples 'releases sort order' do
+ it "sorts the releases #{description}" do
+ card_titles = page.all('.release-block .card-title', minimum: expected_releases.count)
- before do
- sort_page by: 'Created date', direction: :ascending
+ card_titles.each_with_index do |title, index|
+ expect(title).to have_content(expected_releases[index].name)
end
-
- it_behaves_like 'releases sort order'
end
end
- end
- context('when the user is a guest') do
- before do
- sign_in(guest)
- end
+ context "when the page is sorted by the default sort order" do
+ let(:expected_releases) { [release_v3, release_v2, release_v1] }
- it 'renders release info except for Git-related data' do
- visit project_releases_path(project)
+ it_behaves_like 'releases sort order'
+ end
- within('.release-block', match: :first) do
- expect(page).to have_content(release_v3.description)
+ context "when the page is sorted by created_at ascending " do
+ let(:expected_releases) { [release_v2, release_v1, release_v3] }
- # The following properties (sometimes) include Git info,
- # so they are not rendered for Guest users
- expect(page).not_to have_content(release_v3.name)
- expect(page).not_to have_content(release_v3.tag)
- expect(page).not_to have_content(release_v3.commit.short_id)
+ before do
+ sort_page by: 'Created date', direction: :ascending
end
+
+ it_behaves_like 'releases sort order'
end
end
end
- context 'when the graphql_releases_page feature flag is enabled' do
- it_behaves_like 'releases page'
- end
-
- context 'when the graphql_releases_page feature flag is disabled' do
+ context('when the user is a guest') do
before do
- stub_feature_flags(graphql_releases_page: false)
+ sign_in(guest)
end
- it_behaves_like 'releases page'
+ it 'renders release info except for Git-related data' do
+ visit project_releases_path(project)
+
+ within('.release-block', match: :first) do
+ expect(page).to have_content(release_v3.description)
+
+ # The following properties (sometimes) include Git info,
+ # so they are not rendered for Guest users
+ expect(page).not_to have_content(release_v3.name)
+ expect(page).not_to have_content(release_v3.tag)
+ expect(page).not_to have_content(release_v3.commit.short_id)
+ end
+ end
end
end
diff --git a/spec/features/projects/services/user_activates_issue_tracker_spec.rb b/spec/features/projects/services/user_activates_issue_tracker_spec.rb
index 1aec8883395..019d50a497b 100644
--- a/spec/features/projects/services/user_activates_issue_tracker_spec.rb
+++ b/spec/features/projects/services/user_activates_issue_tracker_spec.rb
@@ -87,6 +87,6 @@ RSpec.describe 'User activates issue tracker', :js do
it_behaves_like 'external issue tracker activation', tracker: 'Redmine'
it_behaves_like 'external issue tracker activation', tracker: 'YouTrack', skip_new_issue_url: true
it_behaves_like 'external issue tracker activation', tracker: 'Bugzilla'
- it_behaves_like 'external issue tracker activation', tracker: 'Custom Issue Tracker'
+ it_behaves_like 'external issue tracker activation', tracker: 'Custom issue tracker'
it_behaves_like 'external issue tracker activation', tracker: 'EWM', skip_test: true
end
diff --git a/spec/features/projects/settings/access_tokens_spec.rb b/spec/features/projects/settings/access_tokens_spec.rb
index 8083c851bb7..76d5d7308d1 100644
--- a/spec/features/projects/settings/access_tokens_spec.rb
+++ b/spec/features/projects/settings/access_tokens_spec.rb
@@ -99,7 +99,7 @@ RSpec.describe 'Project > Settings > Access Tokens', :js do
visit project_settings_access_tokens_path(personal_project)
expect(page).to have_selector('#new_project_access_token')
- expect(page).to have_text('You can generate an access token scoped to this project for each application to use the GitLab API.')
+ expect(page).to have_text('Generate project access tokens scoped to this project for your applications that need access to the GitLab API.')
end
end
diff --git a/spec/features/projects/settings/operations_settings_spec.rb b/spec/features/projects/settings/monitor_settings_spec.rb
index ca976997142..64138e0aeca 100644
--- a/spec/features/projects/settings/operations_settings_spec.rb
+++ b/spec/features/projects/settings/monitor_settings_spec.rb
@@ -3,25 +3,35 @@
require 'spec_helper'
RSpec.describe 'Projects > Settings > For a forked project', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository, create_templates: :issue) }
- let(:role) { :maintainer }
+ let_it_be(:project) { create(:project, :repository, create_templates: :issue) }
+
+ let(:user) { project.owner}
before do
sign_in(user)
- project.add_role(user, role)
end
- describe 'Sidebar > Operations' do
- it 'renders the settings link in the sidebar' do
+ describe 'Sidebar > Monitor' do
+ it 'renders the menu in the sidebar' do
visit project_path(project)
wait_for_requests
- expect(page).to have_selector('a[title="Operations"]', visible: false)
+ expect(page).to have_selector('.sidebar-sub-level-items a[aria-label="Monitor"]', text: 'Monitor', visible: false)
+ end
+
+ context 'when feature flag sidebar_refactor is disabled' do
+ it 'renders the menu "Operations" in the sidebar' do
+ stub_feature_flags(sidebar_refactor: false)
+
+ visit project_path(project)
+ wait_for_requests
+
+ expect(page).to have_selector('.sidebar-sub-level-items a[aria-label="Operations"]', text: 'Operations', visible: false)
+ end
end
end
- describe 'Settings > Operations' do
+ describe 'Settings > Monitor' do
describe 'Incidents' do
let(:create_issue) { 'Create an incident. Incidents are created for each alert triggered.' }
let(:send_email) { 'Send a single email notification to Owners and Maintainers for new alerts.' }
diff --git a/spec/features/projects/settings/packages_settings_spec.rb b/spec/features/projects/settings/packages_settings_spec.rb
index 0b40cbee582..62f31fd027b 100644
--- a/spec/features/projects/settings/packages_settings_spec.rb
+++ b/spec/features/projects/settings/packages_settings_spec.rb
@@ -3,36 +3,32 @@
require 'spec_helper'
RSpec.describe 'Projects > Settings > Packages', :js do
- let(:project) { create(:project) }
- let(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+
+ let(:user) { project.owner }
before do
sign_in(user)
- project.add_maintainer(user)
+
+ stub_config(packages: { enabled: packages_enabled })
+
+ visit edit_project_path(project)
end
context 'Packages enabled in config' do
- before do
- allow(Gitlab.config.packages).to receive(:enabled).and_return(true)
- end
+ let(:packages_enabled) { true }
it 'displays the packages toggle button' do
- visit edit_project_path(project)
-
- expect(page).to have_content('Packages')
+ expect(page).to have_button('Packages', class: 'gl-toggle')
expect(page).to have_selector('input[name="project[packages_enabled]"] + button', visible: true)
end
end
context 'Packages disabled in config' do
- before do
- allow(Gitlab.config.packages).to receive(:enabled).and_return(false)
- end
+ let(:packages_enabled) { false }
it 'does not show up in UI' do
- visit edit_project_path(project)
-
- expect(page).not_to have_content('Packages')
+ expect(page).not_to have_button('Packages', class: 'gl-toggle')
end
end
end
diff --git a/spec/features/projects/settings/project_settings_spec.rb b/spec/features/projects/settings/project_settings_spec.rb
index cd1c9ecde9c..71b319d192c 100644
--- a/spec/features/projects/settings/project_settings_spec.rb
+++ b/spec/features/projects/settings/project_settings_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
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.gl-button.js-settings-toggle') }
diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb
index bc60cdd2f8e..6a2769d11fd 100644
--- a/spec/features/projects/settings/registry_settings_spec.rb
+++ b/spec/features/projects/settings/registry_settings_spec.rb
@@ -11,105 +11,125 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p
let(:container_registry_enabled) { true }
let(:container_registry_enabled_on_project) { true }
- subject { visit project_settings_ci_cd_path(project) }
+ shared_examples 'an expiration policy form' do
+ before do
+ project.update!(container_registry_enabled: container_registry_enabled_on_project)
+ project.container_expiration_policy.update!(enabled: true)
- before do
- project.update!(container_registry_enabled: container_registry_enabled_on_project)
- project.container_expiration_policy.update!(enabled: true)
+ sign_in(user)
+ stub_container_registry_config(enabled: container_registry_enabled)
+ end
- sign_in(user)
- stub_container_registry_config(enabled: container_registry_enabled)
- end
+ context 'as owner' do
+ it 'shows available section' do
+ subject
- context 'as owner' do
- it 'shows available section' do
- subject
+ settings_block = find('#js-registry-policies')
+ expect(settings_block).to have_text 'Clean up image tags'
+ end
- settings_block = find('#js-registry-policies')
- expect(settings_block).to have_text 'Clean up image tags'
- end
+ it 'saves cleanup policy submit the form' do
+ subject
- it 'saves cleanup policy submit the form' do
- subject
+ within '#js-registry-policies' do
+ select('Every day', from: 'Run cleanup')
+ select('50 tags per image name', from: 'Keep the most recent:')
+ fill_in('Keep tags matching:', with: 'stable')
+ select('7 days', from: 'Remove tags older than:')
+ fill_in('Remove tags matching:', with: '.*-production')
+
+ submit_button = find('[data-testid="save-button"')
+ expect(submit_button).not_to be_disabled
+ submit_button.click
+ end
- within '#js-registry-policies' do
- select('Every day', from: 'Run cleanup')
- select('50 tags per image name', from: 'Keep the most recent:')
- fill_in('Keep tags matching:', with: 'stable')
- select('7 days', from: 'Remove tags older than:')
- fill_in('Remove tags matching:', with: '.*-production')
+ expect(find('.gl-toast')).to have_content('Cleanup policy successfully saved.')
+ end
- submit_button = find('[data-testid="save-button"')
- expect(submit_button).not_to be_disabled
- submit_button.click
+ it 'does not save cleanup policy submit form with invalid regex' do
+ subject
+
+ within '#js-registry-policies' do
+ fill_in('Remove tags matching:', with: '*-production')
+
+ submit_button = find('[data-testid="save-button"')
+ expect(submit_button).not_to be_disabled
+ submit_button.click
+ end
+
+ expect(find('.gl-toast')).to have_content('Something went wrong while updating the cleanup policy.')
end
- toast = find('.gl-toast')
- expect(toast).to have_content('Cleanup policy successfully saved.')
end
- it 'does not save cleanup policy submit form with invalid regex' do
- subject
+ context 'with a project without expiration policy' do
+ where(:application_setting, :feature_flag, :result) do
+ true | true | :available_section
+ true | false | :available_section
+ false | true | :available_section
+ false | false | :disabled_message
+ end
- within '#js-registry-policies' do
- fill_in('Remove tags matching:', with: '*-production')
+ with_them do
+ before do
+ project.container_expiration_policy.destroy!
+ stub_feature_flags(container_expiration_policies_historic_entry: false)
+ stub_application_setting(container_expiration_policies_enable_historic_entries: application_setting)
+ stub_feature_flags(container_expiration_policies_historic_entry: project) if feature_flag
+ end
- submit_button = find('[data-testid="save-button"')
- expect(submit_button).not_to be_disabled
- submit_button.click
+ it 'displays the expected result' do
+ subject
+
+ within '#js-registry-policies' do
+ case result
+ when :available_section
+ expect(find('[data-testid="enable-toggle"]')).to have_content('Disabled - Tags will not be automatically deleted.')
+ when :disabled_message
+ expect(find('.gl-alert-title')).to have_content('Cleanup policy for tags is disabled')
+ end
+ end
+ end
end
- toast = find('.gl-toast')
- expect(toast).to have_content('Something went wrong while updating the cleanup policy.')
end
- end
- context 'with a project without expiration policy' do
- where(:application_setting, :feature_flag, :result) do
- true | true | :available_section
- true | false | :available_section
- false | true | :available_section
- false | false | :disabled_message
- end
+ context 'when registry is disabled' do
+ let(:container_registry_enabled) { false }
+
+ it 'does not exists' do
+ subject
- with_them do
- before do
- project.container_expiration_policy.destroy!
- stub_feature_flags(container_expiration_policies_historic_entry: false)
- stub_application_setting(container_expiration_policies_enable_historic_entries: application_setting)
- stub_feature_flags(container_expiration_policies_historic_entry: project) if feature_flag
+ expect(page).not_to have_selector('#js-registry-policies')
end
+ end
- it 'displays the expected result' do
+ context 'when container registry is disabled on project' do
+ let(:container_registry_enabled_on_project) { false }
+
+ it 'does not exists' do
subject
- within '#js-registry-policies' do
- case result
- when :available_section
- expect(find('[data-testid="enable-toggle"]')).to have_content('Disabled - Tags will not be automatically deleted.')
- when :disabled_message
- expect(find('.gl-alert-title')).to have_content('Cleanup policy for tags is disabled')
- end
- end
+ expect(page).not_to have_selector('#js-registry-policies')
end
end
end
- context 'when registry is disabled' do
- let(:container_registry_enabled) { false }
+ context 'with sidebar feature flag off' do
+ subject { visit project_settings_ci_cd_path(project) }
- it 'does not exists' do
- subject
-
- expect(page).not_to have_selector('#js-registry-policies')
+ before do
+ stub_feature_flags(sidebar_refactor: false)
end
- end
- context 'when container registry is disabled on project' do
- let(:container_registry_enabled_on_project) { false }
+ it_behaves_like 'an expiration policy form'
+ end
- it 'does not exists' do
- subject
+ context 'with sidebar feature flag on' do
+ subject { visit project_settings_packages_and_registries_path(project) }
- expect(page).not_to have_selector('#js-registry-policies')
+ before do
+ stub_feature_flags(sidebar_refactor: true)
end
+
+ it_behaves_like 'an expiration policy form'
end
end
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index 2f257d299d8..f420a8a76b9 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -42,6 +42,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
context 'Deploy Keys', :js do
let_it_be(:private_deploy_key) { create(:deploy_key, title: 'private_deploy_key', public: false) }
let_it_be(:public_deploy_key) { create(:another_deploy_key, title: 'public_deploy_key', public: true) }
+
let(:new_ssh_key) { attributes_for(:key)[:key] }
it 'get list of keys' do
@@ -116,7 +117,8 @@ RSpec.describe 'Projects > Settings > Repository settings' do
project.deploy_keys << private_deploy_key
visit project_settings_repository_path(project)
- accept_confirm { find('.deploy-key', text: private_deploy_key.title).find('[data-testid="remove-icon"]').click }
+ click_button 'Remove'
+ click_button 'Remove deploy key'
expect(page).not_to have_content(private_deploy_key.title)
end
diff --git a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
index ebda5c9ff59..bf90e86c263 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
@@ -163,7 +163,8 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
click_on('Save changes')
end
- find('.flash-notice')
+ wait_for_requests
+
radio = find_field('project_project_setting_attributes_squash_option_default_on')
expect(radio).to be_checked
@@ -178,7 +179,8 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
click_on('Save changes')
end
- find('.flash-notice')
+ wait_for_requests
+
radio = find_field('project_project_setting_attributes_squash_option_always')
expect(radio).to be_checked
@@ -193,7 +195,8 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
click_on('Save changes')
end
- find('.flash-notice')
+ wait_for_requests
+
radio = find_field('project_project_setting_attributes_squash_option_never')
expect(radio).to be_checked
@@ -220,7 +223,8 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
click_on('Save changes')
end
- find('.flash-notice')
+ wait_for_requests
+
radio = find_field('project_project_setting_attributes_mr_default_target_self_true')
expect(radio).to be_checked
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 b237e7e8ce7..be4b6d6b82d 100644
--- a/spec/features/projects/settings/user_manages_project_members_spec.rb
+++ b/spec/features/projects/settings/user_manages_project_members_spec.rb
@@ -38,16 +38,12 @@ RSpec.describe 'Projects > Settings > User manages project members' do
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))
- page.within('.invite-users-form') do
- click_link('Import')
- end
+ click_link('Import a project')
select2(project2.id, from: '#source_project_id')
click_button('Import project members')
@@ -55,6 +51,28 @@ RSpec.describe 'Projects > Settings > User manages project members' do
expect(find_member_row(user_mike)).to have_content('Reporter')
end
+ describe 'when the :invite_members_group_modal is disabled' do
+ before do
+ stub_feature_flags(invite_members_group_modal: false)
+ end
+
+ it 'imports a team from another project', :js do
+ project2.add_maintainer(user)
+ project2.add_reporter(user_mike)
+
+ visit(project_project_members_path(project))
+
+ page.within('.invite-users-form') do
+ click_link('Import')
+ end
+
+ select2(project2.id, from: '#source_project_id')
+ click_button('Import project members')
+
+ expect(find_member_row(user_mike)).to have_content('Reporter')
+ end
+ end
+
it 'shows all members of project shared group', :js do
group.add_owner(user)
group.add_developer(user_dmitriy)
diff --git a/spec/features/projects/snippets/user_views_snippets_spec.rb b/spec/features/projects/snippets/user_views_snippets_spec.rb
index bc8cba1dc31..40539b43ed5 100644
--- a/spec/features/projects/snippets/user_views_snippets_spec.rb
+++ b/spec/features/projects/snippets/user_views_snippets_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Projects > Snippets > User views snippets' do
let_it_be(:project) { create(:project) }
+
let(:user) { create(:user) }
def visit_project_snippets
diff --git a/spec/features/projects/user_changes_project_visibility_spec.rb b/spec/features/projects/user_changes_project_visibility_spec.rb
index 6935ad4be02..39b8cddd005 100644
--- a/spec/features/projects/user_changes_project_visibility_spec.rb
+++ b/spec/features/projects/user_changes_project_visibility_spec.rb
@@ -28,7 +28,9 @@ RSpec.describe 'User changes public project visibility', :js do
click_button 'Reduce project visibility'
end
- expect(page).to have_text("Project '#{project.name}' was successfully updated")
+ wait_for_requests
+
+ expect(project.reload).to be_private
end
end
diff --git a/spec/features/projects/user_sees_sidebar_spec.rb b/spec/features/projects/user_sees_sidebar_spec.rb
index ff6217d02a7..e2498928fa0 100644
--- a/spec/features/projects/user_sees_sidebar_spec.rb
+++ b/spec/features/projects/user_sees_sidebar_spec.rb
@@ -198,7 +198,7 @@ RSpec.describe 'Projects > User sees sidebar' do
expect(page).to have_content 'Project'
expect(page).to have_content 'Issues'
expect(page).to have_content 'Wiki'
- expect(page).to have_content 'Operations'
+ expect(page).to have_content 'Monitor'
expect(page).not_to have_content 'Repository'
expect(page).not_to have_content 'CI/CD'
diff --git a/spec/features/projects/user_sees_user_popover_spec.rb b/spec/features/projects/user_sees_user_popover_spec.rb
index e357824a533..db451578ff8 100644
--- a/spec/features/projects/user_sees_user_popover_spec.rb
+++ b/spec/features/projects/user_sees_user_popover_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe 'User sees user popover', :js do
include Spec::Support::Helpers::Features::NotesHelpers
let_it_be(:project) { create(:project, :repository) }
+
let(:user) { project.creator }
let(:merge_request) do
create(:merge_request, source_project: project, target_project: project)
diff --git a/spec/features/projects/user_uses_shortcuts_spec.rb b/spec/features/projects/user_uses_shortcuts_spec.rb
index b6fde19e0d4..1350ecf6e75 100644
--- a/spec/features/projects/user_uses_shortcuts_spec.rb
+++ b/spec/features/projects/user_uses_shortcuts_spec.rb
@@ -68,14 +68,27 @@ RSpec.describe 'User uses shortcuts', :js do
end
context 'when navigating to the Project pages' do
- it 'redirects to the details page' do
+ it 'redirects to the project page' do
visit project_issues_path(project)
find('body').native.send_key('g')
find('body').native.send_key('p')
expect(page).to have_active_navigation('Project')
- expect(page).to have_active_sub_navigation('Details')
+ end
+
+ context 'when feature flag :sidebar_refactor is disabled' do
+ it 'redirects to the details page' do
+ stub_feature_flags(sidebar_refactor: false)
+
+ visit project_issues_path(project)
+
+ find('body').native.send_key('g')
+ find('body').native.send_key('p')
+
+ expect(page).to have_active_navigation('Project')
+ expect(page).to have_active_sub_navigation('Details')
+ end
end
it 'redirects to the activity page' do
@@ -165,28 +178,62 @@ RSpec.describe 'User uses shortcuts', :js do
end
end
- context 'when navigating to the Operations pages' do
+ context 'when navigating to the Deployments page' do
+ it 'redirects to the Environments page' do
+ find('body').native.send_key('g')
+ find('body').native.send_key('e')
+
+ expect(page).to have_active_navigation('Deployments')
+ expect(page).to have_active_sub_navigation('Environments')
+ end
+ end
+
+ context 'when navigating to the Monitor pages' do
it 'redirects to the Metrics page' do
find('body').native.send_key('g')
find('body').native.send_key('l')
- expect(page).to have_active_navigation('Operations')
+ expect(page).to have_active_navigation('Monitor')
expect(page).to have_active_sub_navigation('Metrics')
end
- it 'redirects to the Environments page' do
- find('body').native.send_key('g')
- find('body').native.send_key('e')
+ context 'when feature flag :sidebar_refactor is disabled' do
+ before do
+ stub_feature_flags(sidebar_refactor: false)
+ end
- expect(page).to have_active_navigation('Operations')
- expect(page).to have_active_sub_navigation('Environments')
+ it 'redirects to the Operations page' do
+ find('body').native.send_key('g')
+ find('body').native.send_key('l')
+
+ expect(page).to have_active_navigation('Operations')
+ expect(page).to have_active_sub_navigation('Metrics')
+ end
+
+ it 'redirects to the Kubernetes page with active Operations' do
+ find('body').native.send_key('g')
+ find('body').native.send_key('k')
+
+ expect(page).to have_active_navigation('Operations')
+ expect(page).to have_active_sub_navigation('Kubernetes')
+ end
+
+ it 'redirects to the Environments page' do
+ find('body').native.send_key('g')
+ find('body').native.send_key('e')
+
+ expect(page).to have_active_navigation('Operations')
+ expect(page).to have_active_sub_navigation('Environments')
+ end
end
+ end
+ context 'when navigating to the Infrastructure pages' do
it 'redirects to the Kubernetes page' do
find('body').native.send_key('g')
find('body').native.send_key('k')
- expect(page).to have_active_navigation('Operations')
+ expect(page).to have_active_navigation('Infrastructure')
expect(page).to have_active_sub_navigation('Kubernetes')
end
end
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index acfb7c2602a..b61a769185e 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -160,50 +160,92 @@ RSpec.describe 'Runners' do
end
end
- context 'when application settings have shared_runners_text' do
- let(:shared_runners_text) { 'custom **shared** runners description' }
- let(:shared_runners_html) { 'custom shared runners description' }
+ context 'shared runner text' do
+ context 'when application settings have no shared_runners_text' do
+ it 'user sees default shared runners description' do
+ visit project_runners_path(project)
- before do
- stub_application_setting(shared_runners_text: shared_runners_text)
+ page.within("[data-testid='shared-runners-description']") do
+ expect(page).to have_content('The same shared runner executes code from multiple projects')
+ end
+ end
end
- it 'user sees shared runners description' do
- visit project_runners_path(project)
+ context 'when application settings have shared_runners_text' do
+ let(:shared_runners_text) { 'custom **shared** runners description' }
+ let(:shared_runners_html) { 'custom shared runners description' }
+
+ before do
+ stub_application_setting(shared_runners_text: shared_runners_text)
+ end
+
+ it 'user sees shared runners description' do
+ visit project_runners_path(project)
- expect(page.find('.shared-runners-description')).to have_content(shared_runners_html)
+ page.within("[data-testid='shared-runners-description']") do
+ expect(page).not_to have_content('The same shared runner executes code from multiple projects')
+ expect(page).to have_content(shared_runners_html)
+ end
+ end
end
- end
- end
- context 'when a project has disabled shared_runners' do
- let(:project) { create(:project, shared_runners_enabled: false) }
+ context 'when application settings have an unsafe link in shared_runners_text' do
+ let(:shared_runners_text) { '<a href="javascript:alert(\'xss\')">link</a>' }
- context 'when feature flag: vueify_shared_runners_toggle is disabled' do
- before do
- stub_feature_flags(vueify_shared_runners_toggle: false)
- project.add_maintainer(user)
+ before do
+ stub_application_setting(shared_runners_text: shared_runners_text)
+ end
+
+ it 'user sees no link' do
+ visit project_runners_path(project)
+
+ page.within("[data-testid='shared-runners-description']") do
+ expect(page).to have_content('link')
+ expect(page).not_to have_link('link')
+ end
+ end
end
- it 'user enables shared runners' do
- visit project_runners_path(project)
+ context 'when application settings have an unsafe image in shared_runners_text' do
+ let(:shared_runners_text) { '<img src="404.png" onerror="alert(\'xss\')"/>' }
+
+ before do
+ stub_application_setting(shared_runners_text: shared_runners_text)
+ end
- click_on 'Enable shared runners'
+ it 'user sees image safely' do
+ visit project_runners_path(project)
- expect(page.find('.shared-runners-description')).to have_content('Disable shared runners')
- expect(page).not_to have_selector('#toggle-shared-runners-form')
+ page.within("[data-testid='shared-runners-description']") do
+ expect(page).to have_css('img')
+ expect(page).not_to have_css('img[onerror]')
+ end
+ end
end
end
+ end
+
+ context 'enable shared runners in project settings', :js do
+ before do
+ project.add_maintainer(user)
+
+ visit project_runners_path(project)
+ end
- context 'when feature flag: vueify_shared_runners_toggle is enabled' do
- before do
- project.add_maintainer(user)
+ context 'when a project has enabled shared_runners' do
+ let(:project) { create(:project, shared_runners_enabled: true) }
+
+ it 'shared runners toggle is on' do
+ expect(page).to have_selector('[data-testid="toggle-shared-runners"]')
+ expect(page).to have_selector('[data-testid="toggle-shared-runners"] .is-checked')
end
+ end
- it 'user enables shared runners' do
- visit project_runners_path(project)
+ context 'when a project has disabled shared_runners' do
+ let(:project) { create(:project, shared_runners_enabled: false) }
- expect(page).to have_selector('#toggle-shared-runners-form')
+ it 'shared runners toggle is off' do
+ expect(page).not_to have_selector('[data-testid="toggle-shared-runners"] .is-checked')
end
end
end
diff --git a/spec/features/snippets/spam_snippets_spec.rb b/spec/features/snippets/spam_snippets_spec.rb
index 54a56ac962c..3748a916780 100644
--- a/spec/features/snippets/spam_snippets_spec.rb
+++ b/spec/features/snippets/spam_snippets_spec.rb
@@ -18,6 +18,7 @@ RSpec.describe 'snippet editor with spam', skip: "Will be handled in https://git
Gitlab::CurrentSettings.update!(
akismet_enabled: true,
akismet_api_key: 'testkey',
+ spam_check_api_key: 'testkey',
recaptcha_enabled: true,
recaptcha_site_key: 'test site key',
recaptcha_private_key: 'test private key'
diff --git a/spec/features/unsubscribe_links_spec.rb b/spec/features/unsubscribe_links_spec.rb
index 966d90ab16b..b2d0f29808c 100644
--- a/spec/features/unsubscribe_links_spec.rb
+++ b/spec/features/unsubscribe_links_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'Unsubscribe links', :sidekiq_might_not_need_inline do
let(:author) { create(:user) }
let(:project) { create(:project, :public) }
let(:params) { { title: 'A bug!', description: 'Fix it!', assignees: [recipient] } }
- let(:issue) { Issues::CreateService.new(project, author, params).execute }
+ let(:issue) { Issues::CreateService.new(project: project, current_user: author, params: params).execute }
let(:mail) { ActionMailer::Base.deliveries.last }
let(:body) { Capybara::Node::Simple.new(mail.default_part_body.to_s) }
diff --git a/spec/features/user_can_display_performance_bar_spec.rb b/spec/features/user_can_display_performance_bar_spec.rb
index b8f41925156..14b5964686f 100644
--- a/spec/features/user_can_display_performance_bar_spec.rb
+++ b/spec/features/user_can_display_performance_bar_spec.rb
@@ -47,7 +47,7 @@ RSpec.describe 'User can display performance bar', :js do
end
end
- let(:group) { create(:group) }
+ let_it_be(:group) { create(:group) }
before do
allow(GitlabPerformanceBarStatsWorker).to receive(:perform_in)
@@ -123,4 +123,38 @@ RSpec.describe 'User can display performance bar', :js do
end
end
end
+
+ context 'flamegraphs' do
+ let_it_be(:user) { create(:user) }
+
+ before_all do
+ group.add_guest(user)
+ end
+
+ context 'when user has access' do
+ before do
+ stub_application_setting(performance_bar_allowed_group_id: group.id)
+
+ Warden.on_next_request do |proxy|
+ proxy.set_user(user)
+ end
+ end
+
+ it 'renders flamegraph when requested' do
+ visit root_path(performance_bar: 'flamegraph')
+
+ page.within_frame 'speedscope-iframe' do
+ expect(page).to have_content('Flamegraph for /')
+ end
+ end
+ end
+
+ context 'when user does not have access' do
+ it 'renders the original page' do
+ visit root_path(performance_bar: 'flamegraph')
+
+ expect(page).not_to have_selector('iframe#speedscope-iframe')
+ end
+ end
+ end
end
diff --git a/spec/features/users/add_email_to_existing_account_spec.rb b/spec/features/users/add_email_to_existing_account_spec.rb
index 9130b96b0e3..cf78fc4587f 100644
--- a/spec/features/users/add_email_to_existing_account_spec.rb
+++ b/spec/features/users/add_email_to_existing_account_spec.rb
@@ -4,13 +4,25 @@ require 'spec_helper'
RSpec.describe 'AdditionalEmailToExistingAccount' do
describe 'add secondary email associated with account' do
- let(:user) { create(:user) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:email) { create(:email, user: user) }
- it 'verifies confirmation of additional email' do
+ before do
sign_in(user)
+ end
+
+ it 'verifies confirmation of additional email' do
+ visit email_confirmation_path(confirmation_token: email.confirmation_token)
+
+ expect(page).to have_content 'Your email address has been successfully confirmed.'
+ end
+
+ it 'accepts any pending invites for an email confirmation' do
+ member = create(:group_member, :invited, invite_email: email.email)
- email = create(:email, user: user)
visit email_confirmation_path(confirmation_token: email.confirmation_token)
+
+ expect(member.reload.user).to eq(user)
expect(page).to have_content 'Your email address has been successfully confirmed.'
end
end
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index 5f70517224e..17a6abb99e0 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -57,6 +57,12 @@ RSpec.describe 'Signup' do
fill_in 'new_user_password', with: new_user.password
end
+ def confirm_email
+ new_user_token = User.find_by_email(new_user.email).confirmation_token
+
+ visit user_confirmation_path(confirmation_token: new_user_token)
+ end
+
describe 'username validation', :js do
before do
visit new_user_registration_path
@@ -191,7 +197,7 @@ RSpec.describe 'Signup' do
stub_feature_flags(soft_email_confirmation: false)
end
- it 'creates the user account and sends a confirmation email' do
+ it 'creates the user account and sends a confirmation email, and pre-fills email address after confirming' do
visit new_user_registration_path
fill_in_signup_form
@@ -199,6 +205,10 @@ RSpec.describe 'Signup' do
expect { click_button 'Register' }.to change { User.count }.by(1)
expect(current_path).to eq users_almost_there_path
expect(page).to have_content('Please check your email to confirm your account')
+
+ confirm_email
+
+ expect(find_field('Username or email').value).to eq(new_user.email)
end
end
diff --git a/spec/features/users/user_browses_projects_on_user_page_spec.rb b/spec/features/users/user_browses_projects_on_user_page_spec.rb
index 7d05b2ae27a..ded90be3924 100644
--- a/spec/features/users/user_browses_projects_on_user_page_spec.rb
+++ b/spec/features/users/user_browses_projects_on_user_page_spec.rb
@@ -125,7 +125,7 @@ RSpec.describe 'Users > User browses projects on user page', :js do
end
before do
- Issues::CreateService.new(contributed_project, user, { title: 'Bug in old browser' }).execute
+ Issues::CreateService.new(project: contributed_project, current_user: user, params: { title: 'Bug in old browser' }).execute
event = create(:push_event, project: contributed_project, author: user)
create(:push_event_payload, event: event, commit_count: 3)
end
diff --git a/spec/features/whats_new_spec.rb b/spec/features/whats_new_spec.rb
index 55b96361f03..2938ea1b1e8 100644
--- a/spec/features/whats_new_spec.rb
+++ b/spec/features/whats_new_spec.rb
@@ -34,6 +34,24 @@ RSpec.describe "renders a `whats new` dropdown item" do
sign_in(user)
end
+ it 'renders dropdown item when feature enabled' do
+ Gitlab::CurrentSettings.update!(whats_new_variant: ApplicationSetting.whats_new_variants[:all_tiers])
+
+ visit root_dashboard_path
+ find('.header-help-dropdown-toggle').click
+
+ expect(page).to have_button(text: "What's new")
+ end
+
+ it 'does not render dropdown item when feature disabled' do
+ Gitlab::CurrentSettings.update!(whats_new_variant: ApplicationSetting.whats_new_variants[:disabled])
+
+ visit root_dashboard_path
+ find('.header-help-dropdown-toggle').click
+
+ expect(page).not_to have_button(text: "What's new")
+ end
+
it 'shows notification dot and count and removes it once viewed' do
visit root_dashboard_path