summaryrefslogtreecommitdiff
path: root/spec/features
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-05-19 07:33:21 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-05-19 07:33:21 +0000
commit36a59d088eca61b834191dacea009677a96c052f (patch)
treee4f33972dab5d8ef79e3944a9f403035fceea43f /spec/features
parenta1761f15ec2cae7c7f7bbda39a75494add0dfd6f (diff)
downloadgitlab-ce-36a59d088eca61b834191dacea009677a96c052f.tar.gz
Add latest changes from gitlab-org/gitlab@15-0-stable-eev15.0.0-rc42
Diffstat (limited to 'spec/features')
-rw-r--r--spec/features/admin/admin_groups_spec.rb2
-rw-r--r--spec/features/admin/admin_projects_spec.rb4
-rw-r--r--spec/features/admin/admin_requests_profiles_spec.rb136
-rw-r--r--spec/features/admin/admin_runners_spec.rb59
-rw-r--r--spec/features/admin/admin_sees_background_migrations_spec.rb142
-rw-r--r--spec/features/admin/admin_settings_spec.rb55
-rw-r--r--spec/features/admin/clusters/eks_spec.rb32
-rw-r--r--spec/features/admin/users/users_spec.rb2
-rw-r--r--spec/features/dashboard/issuables_counter_spec.rb5
-rw-r--r--spec/features/dashboard/issues_filter_spec.rb4
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb15
-rw-r--r--spec/features/dashboard/todos/todos_sorting_spec.rb22
-rw-r--r--spec/features/dashboard/todos/todos_spec.rb4
-rw-r--r--spec/features/explore/topics_spec.rb4
-rw-r--r--spec/features/frequently_visited_projects_and_groups_spec.rb2
-rw-r--r--spec/features/groups/clusters/eks_spec.rb37
-rw-r--r--spec/features/groups/crm/contacts/create_spec.rb28
-rw-r--r--spec/features/groups/dependency_proxy_spec.rb16
-rw-r--r--spec/features/groups/empty_states_spec.rb18
-rw-r--r--spec/features/groups/group_settings_spec.rb2
-rw-r--r--spec/features/groups/issues_spec.rb65
-rw-r--r--spec/features/groups/members/manage_groups_spec.rb138
-rw-r--r--spec/features/groups/settings/ci_cd_spec.rb65
-rw-r--r--spec/features/groups_spec.rb18
-rw-r--r--spec/features/ide/user_opens_merge_request_spec.rb1
-rw-r--r--spec/features/issuables/issuable_list_spec.rb12
-rw-r--r--spec/features/issuables/sorting_list_spec.rb4
-rw-r--r--spec/features/issue_rebalancing_spec.rb12
-rw-r--r--spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb8
-rw-r--r--spec/features/issues/filtered_search/dropdown_assignee_spec.rb30
-rw-r--r--spec/features/issues/filtered_search/dropdown_author_spec.rb24
-rw-r--r--spec/features/issues/filtered_search/dropdown_base_spec.rb22
-rw-r--r--spec/features/issues/filtered_search/dropdown_emoji_spec.rb32
-rw-r--r--spec/features/issues/filtered_search/dropdown_hint_spec.rb62
-rw-r--r--spec/features/issues/filtered_search/dropdown_label_spec.rb10
-rw-r--r--spec/features/issues/filtered_search/dropdown_milestone_spec.rb14
-rw-r--r--spec/features/issues/filtered_search/dropdown_release_spec.rb16
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb344
-rw-r--r--spec/features/issues/filtered_search/recent_searches_spec.rb97
-rw-r--r--spec/features/issues/filtered_search/search_bar_spec.rb86
-rw-r--r--spec/features/issues/filtered_search/visual_tokens_spec.rb128
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb2
-rw-r--r--spec/features/issues/rss_spec.rb16
-rw-r--r--spec/features/issues/user_bulk_edits_issues_labels_spec.rb8
-rw-r--r--spec/features/issues/user_creates_branch_and_merge_request_spec.rb14
-rw-r--r--spec/features/issues/user_creates_issue_spec.rb6
-rw-r--r--spec/features/issues/user_filters_issues_spec.rb4
-rw-r--r--spec/features/issues/user_scrolls_to_deeplinked_note_spec.rb1
-rw-r--r--spec/features/issues/user_sees_breadcrumb_links_spec.rb6
-rw-r--r--spec/features/issues/user_sorts_issues_spec.rb17
-rw-r--r--spec/features/labels_hierarchy_spec.rb64
-rw-r--r--spec/features/markdown/mermaid_spec.rb45
-rw-r--r--spec/features/merge_request/batch_comments_spec.rb13
-rw-r--r--spec/features/merge_request/close_reopen_report_toggle_spec.rb20
-rw-r--r--spec/features/merge_request/merge_request_discussion_lock_spec.rb93
-rw-r--r--spec/features/merge_request/user_accepts_merge_request_spec.rb18
-rw-r--r--spec/features/merge_request/user_assigns_themselves_spec.rb6
-rw-r--r--spec/features/merge_request/user_awards_emoji_spec.rb4
-rw-r--r--spec/features/merge_request/user_comments_on_diff_spec.rb13
-rw-r--r--spec/features/merge_request/user_creates_image_diff_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_creates_merge_request_spec.rb42
-rw-r--r--spec/features/merge_request/user_customizes_merge_commit_message_spec.rb8
-rw-r--r--spec/features/merge_request/user_edits_merge_request_spec.rb2
-rw-r--r--spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb1
-rw-r--r--spec/features/merge_request/user_manages_subscription_spec.rb45
-rw-r--r--spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb3
-rw-r--r--spec/features/merge_request/user_merges_immediately_spec.rb10
-rw-r--r--spec/features/merge_request/user_merges_merge_request_spec.rb5
-rw-r--r--spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb2
-rw-r--r--spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb13
-rw-r--r--spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb39
-rw-r--r--spec/features/merge_request/user_posts_diff_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb86
-rw-r--r--spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb9
-rw-r--r--spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb43
-rw-r--r--spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb1
-rw-r--r--spec/features/merge_request/user_squashes_merge_request_spec.rb2
-rw-r--r--spec/features/merge_request/user_views_diffs_spec.rb2
-rw-r--r--spec/features/merge_request/user_views_open_merge_request_spec.rb16
-rw-r--r--spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb4
-rw-r--r--spec/features/merge_requests/user_lists_merge_requests_spec.rb14
-rw-r--r--spec/features/merge_requests/user_mass_updates_spec.rb6
-rw-r--r--spec/features/merge_requests/user_sorts_merge_requests_spec.rb33
-rw-r--r--spec/features/monitor_sidebar_link_spec.rb5
-rw-r--r--spec/features/oauth_login_spec.rb55
-rw-r--r--spec/features/profiles/keys_spec.rb2
-rw-r--r--spec/features/profiles/oauth_applications_spec.rb2
-rw-r--r--spec/features/projects/active_tabs_spec.rb2
-rw-r--r--spec/features/projects/blobs/blame_spec.rb67
-rw-r--r--spec/features/projects/ci/editor_spec.rb34
-rw-r--r--spec/features/projects/ci/secure_files_spec.rb44
-rw-r--r--spec/features/projects/clusters/eks_spec.rb36
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb95
-rw-r--r--spec/features/projects/clusters_spec.rb97
-rw-r--r--spec/features/projects/commit/mini_pipeline_graph_spec.rb2
-rw-r--r--spec/features/projects/graph_spec.rb17
-rw-r--r--spec/features/projects/integrations/prometheus_external_alerts_spec.rb34
-rw-r--r--spec/features/projects/integrations/user_activates_prometheus_spec.rb5
-rw-r--r--spec/features/projects/issues/design_management/user_uploads_designs_spec.rb3
-rw-r--r--spec/features/projects/jobs/user_browses_jobs_spec.rb2
-rw-r--r--spec/features/projects/jobs_spec.rb2
-rw-r--r--spec/features/projects/members/groups_with_access_list_spec.rb2
-rw-r--r--spec/features/projects/members/manage_groups_spec.rb (renamed from spec/features/projects/members/invite_group_spec.rb)83
-rw-r--r--spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb2
-rw-r--r--spec/features/projects/members/master_manages_access_requests_spec.rb2
-rw-r--r--spec/features/projects/members/member_leaves_project_spec.rb2
-rw-r--r--spec/features/projects/navbar_spec.rb6
-rw-r--r--spec/features/projects/packages_spec.rb3
-rw-r--r--spec/features/projects/pipelines/legacy_pipeline_spec.rb1073
-rw-r--r--spec/features/projects/pipelines/legacy_pipelines_spec.rb847
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb288
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb26
-rw-r--r--spec/features/projects/serverless/functions_spec.rb88
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb18
-rw-r--r--spec/features/projects/settings/secure_files_settings_spec.rb46
-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.rb2
-rw-r--r--spec/features/projects/show/user_uploads_files_spec.rb12
-rw-r--r--spec/features/projects/snippets/create_snippet_spec.rb9
-rw-r--r--spec/features/projects/tags/user_views_tags_spec.rb30
-rw-r--r--spec/features/projects/tree/tree_show_spec.rb16
-rw-r--r--spec/features/runners_spec.rb202
-rw-r--r--spec/features/security/project/internal_access_spec.rb2
-rw-r--r--spec/features/security/project/private_access_spec.rb4
-rw-r--r--spec/features/security/project/public_access_spec.rb4
-rw-r--r--spec/features/snippets/user_creates_snippet_spec.rb17
-rw-r--r--spec/features/topic_show_spec.rb7
-rw-r--r--spec/features/user_sorts_things_spec.rb15
-rw-r--r--spec/features/users/login_spec.rb2
130 files changed, 3679 insertions, 2164 deletions
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index 3b3289a8487..90dde7340d5 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -212,7 +212,7 @@ RSpec.describe 'Admin Groups' do
it do
visit admin_group_path(group)
- select2(user_selector, from: '#user_ids', multiple: true)
+ select2(user_selector, from: '#user_id', multiple: true)
page.within '#new_project_member' do
select2(Gitlab::Access::REPORTER, from: '#access_level')
end
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb
index b0737377de0..2166edf65ff 100644
--- a/spec/features/admin/admin_projects_spec.rb
+++ b/spec/features/admin/admin_projects_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe "Admin::Projects" do
include Spec::Support::Helpers::ModalHelpers
let(:user) { create :user }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :with_namespace_settings) }
let(:current_user) { create(:admin) }
before do
@@ -82,7 +82,7 @@ RSpec.describe "Admin::Projects" do
describe 'transfer project' do
# The gitlab-shell transfer will fail for a project without a repository
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project, :repository, :with_namespace_settings) }
before do
create(:group, name: 'Web')
diff --git a/spec/features/admin/admin_requests_profiles_spec.rb b/spec/features/admin/admin_requests_profiles_spec.rb
deleted file mode 100644
index e92528d431d..00000000000
--- a/spec/features/admin/admin_requests_profiles_spec.rb
+++ /dev/null
@@ -1,136 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Admin::RequestsProfilesController' do
- let(:tmpdir) { Dir.mktmpdir('profiler-test') }
-
- before do
- stub_const('Gitlab::RequestProfiler::PROFILES_DIR', tmpdir)
- admin = create(:admin)
- sign_in(admin)
- gitlab_enable_admin_mode_sign_in(admin)
- end
-
- after do
- FileUtils.rm_rf(tmpdir)
- end
-
- describe 'GET /admin/requests_profiles' do
- it 'shows the current profile token' do
- allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new)
-
- visit admin_requests_profiles_path
-
- expect(page).to have_content("X-Profile-Token: #{Gitlab::RequestProfiler.profile_token}")
- end
-
- context 'when having multiple profiles' do
- let(:time1) { 1.hour.ago }
- let(:time2) { 2.hours.ago }
-
- let(:profiles) do
- [
- {
- request_path: '/gitlab-org/gitlab-foss',
- name: "|gitlab-org|gitlab-foss_#{time1.to_i}_execution.html",
- created: time1,
- profile_mode: 'Execution'
- },
- {
- request_path: '/gitlab-org/gitlab-foss',
- name: "|gitlab-org|gitlab-foss_#{time2.to_i}_execution.html",
- created: time2,
- profile_mode: 'Execution'
- },
- {
- request_path: '/gitlab-org/gitlab-foss',
- name: "|gitlab-org|gitlab-foss_#{time1.to_i}_memory.html",
- created: time1,
- profile_mode: 'Memory'
- },
- {
- request_path: '/gitlab-org/gitlab-foss',
- name: "|gitlab-org|gitlab-foss_#{time2.to_i}_memory.html",
- created: time2,
- profile_mode: 'Memory'
- },
- {
- request_path: '/gitlab-org/infrastructure',
- name: "|gitlab-org|infrastructure_#{time1.to_i}_execution.html",
- created: time1,
- profile_mode: 'Execution'
- },
- {
- request_path: '/gitlab-org/infrastructure',
- name: "|gitlab-org|infrastructure_#{time2.to_i}_memory.html",
- created: time2,
- profile_mode: 'Memory'
- },
- {
- request_path: '/gitlab-org/infrastructure',
- name: "|gitlab-org|infrastructure_#{time2.to_i}.html",
- created: time2,
- profile_mode: 'Unknown'
- }
- ]
- end
-
- before do
- profiles.each do |profile|
- FileUtils.touch(File.join(Gitlab::RequestProfiler::PROFILES_DIR, profile[:name]))
- end
- end
-
- it 'lists all available profiles' do
- visit admin_requests_profiles_path
-
- profiles.each do |profile|
- within('.card', text: profile[:request_path]) do
- expect(page).to have_selector(
- "a[href='#{admin_requests_profile_path(profile[:name])}']",
- text: "#{profile[:created].to_s(:long)} #{profile[:profile_mode]}")
- end
- end
- end
- end
- end
-
- describe 'GET /admin/requests_profiles/:profile' do
- context 'when a profile exists' do
- before do
- File.write("#{Gitlab::RequestProfiler::PROFILES_DIR}/#{profile}", content)
- end
-
- context 'when is valid call stack profile' do
- let(:content) { 'This is a call stack request profile' }
- let(:profile) { "|gitlab-org|gitlab-ce_#{Time.now.to_i}_execution.html" }
-
- it 'displays the content' do
- visit admin_requests_profile_path(profile)
-
- expect(page).to have_content(content)
- end
- end
-
- context 'when is valid memory profile' do
- let(:content) { 'This is a memory request profile' }
- let(:profile) { "|gitlab-org|gitlab-ce_#{Time.now.to_i}_memory.txt" }
-
- it 'displays the content' do
- visit admin_requests_profile_path(profile)
-
- expect(page).to have_content(content)
- end
- end
- end
-
- context 'when a profile does not exist' do
- it 'shows an error message' do
- visit admin_requests_profile_path('|non|existent_12345.html')
-
- expect(page).to have_content('Profile not found')
- end
- end
- end
-end
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index 7fe49c2571c..e1a1e2bbb2d 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe "Admin Runners" do
include Spec::Support::Helpers::Features::RunnersHelpers
+ include Spec::Support::Helpers::ModalHelpers
let_it_be(:admin) { create(:admin) }
@@ -429,12 +430,6 @@ RSpec.describe "Admin Runners" do
end
context "when visiting outdated URLs" do
- it 'updates NOT_CONNECTED runner status to NEVER_CONNECTED' do
- visit admin_runners_path('status[]': 'NOT_CONNECTED')
-
- expect(page).to have_current_path(admin_runners_path('status[]': 'NEVER_CONTACTED') )
- end
-
it 'updates ACTIVE runner status to paused=false' do
visit admin_runners_path('status[]': 'ACTIVE')
@@ -467,7 +462,7 @@ RSpec.describe "Admin Runners" do
describe 'runner show page breadcrumbs' do
it 'contains the current runner id and token' do
page.within '[data-testid="breadcrumb-links"]' do
- expect(page.find('h2')).to have_link("##{runner.id} (#{runner.short_sha})")
+ expect(page.find('[data-testid="breadcrumb-current-link"]')).to have_link("##{runner.id} (#{runner.short_sha})")
end
end
end
@@ -483,10 +478,28 @@ RSpec.describe "Admin Runners" do
expect(page).to have_content 'Tags tag1'
end
end
+
+ describe 'when a runner is deleted' do
+ before do
+ click_on 'Delete runner'
+
+ within_modal do
+ click_on 'Delete runner'
+ end
+ end
+
+ it 'deletes runner' do
+ expect(page.find('[data-testid="alert-success"]')).to have_content('deleted')
+ end
+
+ it 'redirects to runner list' do
+ expect(current_url).to match(admin_runners_path)
+ end
+ end
end
describe "Runner edit page" do
- let(:runner) { create(:ci_runner) }
+ let(:runner) { create(:ci_runner, :project) }
before do
@project1 = create(:project)
@@ -500,14 +513,29 @@ RSpec.describe "Admin Runners" do
it 'contains the current runner id and token' do
page.within '[data-testid="breadcrumb-links"]' do
expect(page).to have_link("##{runner.id} (#{runner.short_sha})")
- expect(page.find('h2')).to have_content("Edit")
+ expect(page.find('[data-testid="breadcrumb-current-link"]')).to have_content("Edit")
end
end
end
describe 'runner header', :js do
it 'contains the runner status, type and id' do
- expect(page).to have_content("never contacted shared Runner ##{runner.id} created")
+ expect(page).to have_content("never contacted specific Runner ##{runner.id} created")
+ end
+ end
+
+ describe 'when a runner is updated', :js do
+ before do
+ click_on _('Save changes')
+ wait_for_requests
+ end
+
+ it 'show success alert' do
+ expect(page.find('[data-testid="alert-success"]')).to have_content('saved')
+ end
+
+ it 'redirects to runner page' do
+ expect(current_url).to match(admin_runner_path(runner))
end
end
@@ -564,17 +592,6 @@ RSpec.describe "Admin Runners" do
it_behaves_like 'assignable runner'
end
-
- context 'with shared runner' do
- let(:runner) { create(:ci_runner, :instance) }
-
- before do
- @project1.destroy!
- visit edit_admin_runner_path(runner)
- end
-
- it_behaves_like 'assignable runner'
- end
end
describe 'disable/destroy' do
diff --git a/spec/features/admin/admin_sees_background_migrations_spec.rb b/spec/features/admin/admin_sees_background_migrations_spec.rb
index 432721d63ad..8edddcf9a9b 100644
--- a/spec/features/admin/admin_sees_background_migrations_spec.rb
+++ b/spec/features/admin/admin_sees_background_migrations_spec.rb
@@ -31,6 +31,52 @@ RSpec.describe "Admin > Admin sees background migrations" do
end
end
+ it 'can click on a specific migration' do
+ visit admin_background_migrations_path
+
+ within '#content-body' do
+ tab = find_link active_migration.job_class_name
+ tab.click
+
+ expect(page).to have_current_path admin_background_migration_path(active_migration)
+ end
+ end
+
+ it 'can view failed jobs' do
+ visit admin_background_migration_path(failed_migration)
+
+ within '#content-body' do
+ expect(page).to have_content('Failed jobs')
+ expect(page).to have_content('Id')
+ expect(page).to have_content('Started at')
+ expect(page).to have_content('Finished at')
+ expect(page).to have_content('Batch size')
+ end
+ end
+
+ it 'can click on a specific job' do
+ job = create(:batched_background_migration_job, :failed, batched_migration: failed_migration)
+
+ visit admin_background_migration_path(failed_migration)
+
+ within '#content-body' do
+ tab = find_link job.id
+ tab.click
+
+ expect(page).to have_current_path admin_background_migration_batched_job_path(id: job.id, background_migration_id: failed_migration.id)
+ end
+ end
+
+ context 'when there are no failed jobs' do
+ it 'dos not display failed jobs' do
+ visit admin_background_migration_path(active_migration)
+
+ within '#content-body' do
+ expect(page).not_to have_content('Failed jobs')
+ end
+ end
+ end
+
it 'can view queued migrations and pause and resume them' do
visit admin_background_migrations_path
@@ -66,6 +112,17 @@ RSpec.describe "Admin > Admin sees background migrations" do
end
end
+ it 'can fire an action with a database param' do
+ visit admin_background_migrations_path(database: 'main')
+
+ within '#content-body' do
+ tab = find_link 'Failed'
+ tab.click
+
+ expect(page).to have_selector("[method='post'][action='/admin/background_migrations/#{failed_migration.id}/retry?database=main']")
+ end
+ end
+
it 'can view and retry them' do
visit admin_background_migrations_path
@@ -109,4 +166,89 @@ RSpec.describe "Admin > Admin sees background migrations" do
expect(page).to have_content(finished_migration.status_name.to_s)
end
end
+
+ it 'can change tabs and retain database param' do
+ skip_if_multiple_databases_not_setup
+
+ visit admin_background_migrations_path(database: 'ci')
+
+ within '#content-body' do
+ tab = find_link 'Finished'
+ expect(tab[:class]).not_to include('gl-tab-nav-item-active')
+
+ tab.click
+
+ expect(page).to have_current_path(admin_background_migrations_path(tab: 'finished', database: 'ci'))
+ expect(tab[:class]).to include('gl-tab-nav-item-active')
+ end
+ end
+
+ it 'can view documentation from Learn more link' do
+ visit admin_background_migrations_path
+
+ within '#content-body' do
+ expect(page).to have_link('Learn more', href: help_page_path('development/database/batched_background_migrations'))
+ end
+ end
+
+ describe 'selected database toggle', :js do
+ context 'when multi database is not enabled' do
+ before do
+ skip_if_multiple_databases_are_setup
+
+ allow(Gitlab::Database).to receive(:db_config_names).and_return(['main'])
+ end
+
+ it 'does not render the database listbox' do
+ visit admin_background_migrations_path
+
+ expect(page).not_to have_selector('[data-testid="database-listbox"]')
+ end
+ end
+
+ context 'when multi database is enabled' do
+ before do
+ skip_if_multiple_databases_not_setup
+
+ allow(Gitlab::Database).to receive(:db_config_names).and_return(%w[main ci])
+ end
+
+ it 'does render the database listbox' do
+ visit admin_background_migrations_path
+
+ expect(page).to have_selector('[data-testid="database-listbox"]')
+ end
+
+ it 'defaults to main when no parameter is passed' do
+ visit admin_background_migrations_path
+
+ listbox = page.find('[data-testid="database-listbox"]')
+
+ expect(listbox).to have_text('main')
+ end
+
+ it 'shows correct database when a parameter is passed' do
+ visit admin_background_migrations_path(database: 'ci')
+
+ listbox = page.find('[data-testid="database-listbox"]')
+
+ expect(listbox).to have_text('ci')
+ end
+
+ it 'updates the path to correct database when clicking on listbox option' do
+ visit admin_background_migrations_path
+
+ listbox = page.find('[data-testid="database-listbox"]')
+ expect(listbox).to have_text('main')
+
+ listbox.find('button').click
+ listbox.find('li', text: 'ci').click
+ wait_for_requests
+
+ expect(page).to have_current_path(admin_background_migrations_path(database: 'ci'))
+ listbox = page.find('[data-testid="database-listbox"]')
+ expect(listbox).to have_text('ci')
+ end
+ end
+ end
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 4cdc3df978d..79b3f049047 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -71,7 +71,7 @@ RSpec.describe 'Admin updates settings' do
it 'change Visibility and Access Controls' do
page.within('.as-visibility-access') do
- uncheck 'Project export enabled'
+ uncheck 'Enabled'
click_button 'Save changes'
end
@@ -111,6 +111,16 @@ RSpec.describe 'Admin updates settings' do
expect(page).to have_content "Application settings saved successfully"
end
+ it 'change Maximum export size' do
+ page.within('.as-account-limit') do
+ fill_in 'Maximum export size (MB)', with: 25
+ click_button 'Save changes'
+ end
+
+ expect(current_settings.max_export_size).to eq 25
+ expect(page).to have_content "Application settings saved successfully"
+ end
+
it 'change Maximum import size' do
page.within('.as-account-limit') do
fill_in 'Maximum import size (MB)', with: 15
@@ -370,7 +380,7 @@ RSpec.describe 'Admin updates settings' do
expect(current_settings.valid_runner_registrars).to eq(ApplicationSetting::VALID_RUNNER_REGISTRAR_TYPES)
page.within('.as-runner') do
- find_all('.form-check-input').each(&:click)
+ find_all('input[type="checkbox"]').each(&:click)
click_button 'Save changes'
end
@@ -396,7 +406,6 @@ RSpec.describe 'Admin updates settings' do
end
context 'Container Registry' do
- let(:feature_flag_enabled) { true }
let(:client_support) { true }
let(:settings_titles) do
{
@@ -409,18 +418,9 @@ RSpec.describe 'Admin updates settings' do
before do
stub_container_registry_config(enabled: true)
- stub_feature_flags(container_registry_expiration_policies_throttling: feature_flag_enabled)
allow(ContainerRegistry::Client).to receive(:supports_tag_delete?).and_return(client_support)
end
- shared_examples 'not having container registry setting' do |registry_setting|
- it "lacks the container setting #{registry_setting}" do
- visit ci_cd_admin_application_settings_path
-
- expect(page).not_to have_content(settings_titles[registry_setting])
- end
- end
-
%i[container_registry_delete_tags_service_timeout container_registry_expiration_policies_worker_capacity container_registry_cleanup_tags_service_max_list_size].each do |setting|
context "for container registry setting #{setting}" do
it 'changes the setting' do
@@ -434,12 +434,6 @@ RSpec.describe 'Admin updates settings' do
expect(current_settings.public_send(setting)).to eq(400)
expect(page).to have_content "Application settings saved successfully"
end
-
- context 'with feature flag disabled' do
- let(:feature_flag_enabled) { false }
-
- it_behaves_like 'not having container registry setting', setting
- end
end
end
@@ -457,12 +451,6 @@ RSpec.describe 'Admin updates settings' do
expect(current_settings.container_registry_expiration_policies_caching).to eq(!old_value)
expect(page).to have_content "Application settings saved successfully"
end
-
- context 'with feature flag disabled' do
- let(:feature_flag_enabled) { false }
-
- it_behaves_like 'not having container registry setting', :container_registry_expiration_policies_caching
- end
end
end
end
@@ -665,7 +653,7 @@ RSpec.describe 'Admin updates settings' do
visit network_admin_application_settings_path
page.within('.as-issue-limits') do
- fill_in 'Max requests per minute per user', with: 0
+ fill_in 'Maximum number of requests per minute', with: 0
click_button 'Save changes'
end
@@ -673,6 +661,18 @@ RSpec.describe 'Admin updates settings' do
expect(current_settings.issues_create_limit).to eq(0)
end
+ it 'changes Pipelines rate limits settings' do
+ visit network_admin_application_settings_path
+
+ page.within('.as-pipeline-limits') do
+ fill_in 'Maximum number of requests per minute', with: 10
+ click_button 'Save changes'
+ end
+
+ expect(page).to have_content "Application settings saved successfully"
+ expect(current_settings.pipeline_limit_per_project_user_sha).to eq(10)
+ end
+
it 'changes Users API rate limits settings' do
visit network_admin_application_settings_path
@@ -856,10 +856,9 @@ RSpec.describe 'Admin updates settings' do
stub_database_flavor_check
end
- context 'when service data cached', :clean_gitlab_redis_cache do
+ context 'when service data cached', :use_clean_rails_memory_store_caching do
before do
- allow(Rails.cache).to receive(:exist?).with('usage_data').and_return(true)
-
+ visit usage_data_admin_application_settings_path
visit service_usage_data_admin_application_settings_path
end
diff --git a/spec/features/admin/clusters/eks_spec.rb b/spec/features/admin/clusters/eks_spec.rb
deleted file mode 100644
index 4667f9c20a1..00000000000
--- a/spec/features/admin/clusters/eks_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Instance-level AWS EKS Cluster', :js do
- let(:user) { create(:admin) }
-
- before do
- sign_in(user)
- gitlab_enable_admin_mode_sign_in(user)
- stub_application_setting(eks_integration_enabled: true)
- end
-
- context 'when user does not have a cluster and visits group clusters page' do
- before do
- visit admin_clusters_path
-
- click_button(class: 'dropdown-toggle-split')
- click_link 'Create a cluster (deprecated)'
- end
-
- context 'when user creates a cluster on AWS EKS' do
- before do
- click_link 'Amazon EKS'
- end
-
- it 'user sees a form to create an EKS cluster' do
- expect(page).to have_content('Authenticate with Amazon Web Services')
- end
- end
- end
-end
diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb
index 4d9a7f31911..a05e1531949 100644
--- a/spec/features/admin/users/users_spec.rb
+++ b/spec/features/admin/users/users_spec.rb
@@ -548,7 +548,7 @@ RSpec.describe 'Admin::Users' do
end
def check_breadcrumb(content)
- expect(find('.breadcrumbs-sub-title')).to have_content(content)
+ expect(find('[data-testid="breadcrumb-current-link"]')).to have_content(content)
end
end
diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb
index aa445265eec..f8b68be7f93 100644
--- a/spec/features/dashboard/issuables_counter_spec.rb
+++ b/spec/features/dashboard/issuables_counter_spec.rb
@@ -53,6 +53,11 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d
describe 'feature flag mr_attention_requests is enabled' do
before do
merge_request.update!(assignees: [user])
+
+ merge_request.find_assignee(user).update!(state: :attention_requested)
+
+ user.invalidate_attention_requested_count
+
sign_in(user)
end
diff --git a/spec/features/dashboard/issues_filter_spec.rb b/spec/features/dashboard/issues_filter_spec.rb
index 4d59e1ded3d..3c774f8b269 100644
--- a/spec/features/dashboard/issues_filter_spec.rb
+++ b/spec/features/dashboard/issues_filter_spec.rb
@@ -78,14 +78,14 @@ RSpec.describe 'Dashboard Issues filtering', :js do
end
it 'remembers last sorting value' do
- sort_by('Created date')
+ pajamas_sort_by(s_('SortOptions|Created date'))
visit_issues(assignee_username: user.username)
expect(page).to have_button('Created date')
end
it 'keeps sorting issues after visiting Projects Issues page' do
- sort_by('Created date')
+ pajamas_sort_by(s_('SortOptions|Created date'))
visit project_issues_path(project)
expect(page).to have_button('Created date')
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index 7507ef4e453..fd580b679ad 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -112,9 +112,12 @@ RSpec.describe 'Dashboard Merge Requests' do
end
it 'includes assigned and reviewers in badge' do
- within("span[aria-label='#{n_("%d merge request", "%d merge requests", 3) % 3}']") do
- expect(page).to have_content('3')
+ within("span[aria-label='#{n_("%d merge request", "%d merge requests", 0) % 0}']") do
+ expect(page).to have_content('0')
end
+
+ find('.dashboard-shortcuts-merge_requests').click
+
expect(find('.js-assigned-mr-count')).to have_content('2')
expect(find('.js-reviewer-mr-count')).to have_content('1')
end
@@ -165,16 +168,16 @@ RSpec.describe 'Dashboard Merge Requests' do
expect(page).to have_content('Please select at least one filter to see results')
end
- it 'shows sorted merge requests' do
- sort_by('Created date')
+ it 'shows sorted merge requests', :js do
+ pajamas_sort_by(s_('SortOptions|Created date'))
visit merge_requests_dashboard_path(assignee_username: current_user.username)
expect(find('.issues-filters')).to have_content('Created date')
end
- it 'keeps sorting merge requests after visiting Projects MR page' do
- sort_by('Created date')
+ it 'keeps sorting merge requests after visiting Projects MR page', :js do
+ pajamas_sort_by(s_('SortOptions|Created date'))
visit project_merge_requests_path(project)
diff --git a/spec/features/dashboard/todos/todos_sorting_spec.rb b/spec/features/dashboard/todos/todos_sorting_spec.rb
index d0f9a2b35f3..d593031590e 100644
--- a/spec/features/dashboard/todos/todos_sorting_spec.rb
+++ b/spec/features/dashboard/todos/todos_sorting_spec.rb
@@ -23,11 +23,12 @@ RSpec.describe 'Dashboard > User sorts todos' do
let!(:merge_request_1) { create(:merge_request, source_project: project, title: 'merge_request_1') }
before do
- create(:todo, user: user, project: project, target: issue_4, created_at: 5.hours.ago)
- create(:todo, user: user, project: project, target: issue_2, created_at: 4.hours.ago)
- create(:todo, user: user, project: project, target: issue_3, created_at: 3.hours.ago)
- create(:todo, user: user, project: project, target: issue_1, created_at: 2.hours.ago)
- create(:todo, user: user, project: project, target: merge_request_1, created_at: 1.hour.ago)
+ create(:todo, user: user, project: project, target: issue_4, created_at: 5.hours.ago, updated_at: 5.hours.ago)
+ create(:todo, user: user, project: project, target: issue_2, created_at: 4.hours.ago, updated_at: 4.hours.ago)
+ create(:todo, user: user, project: project, target: issue_3, created_at: 3.hours.ago, updated_at: 2.minutes.ago)
+ create(:todo, user: user, project: project, target: issue_1, created_at: 2.hours.ago, updated_at: 2.hours.ago)
+ create(:todo, user: user, project: project, target: merge_request_1, created_at: 1.hour.ago,
+ updated_at: 1.hour.ago)
merge_request_1.labels << label_1
issue_3.labels << label_1
@@ -70,6 +71,17 @@ RSpec.describe 'Dashboard > User sorts todos' do
expect(results_list.all('.todo-title')[3]).to have_content('issue_2')
expect(results_list.all('.todo-title')[4]).to have_content('issue_4')
end
+
+ it 'sorts by newest updated todos first' do
+ click_link 'Updated date'
+
+ results_list = page.find('.todos-list')
+ expect(results_list.all('.todo-title')[0]).to have_content('issue_3')
+ expect(results_list.all('.todo-title')[1]).to have_content('merge_request_1')
+ expect(results_list.all('.todo-title')[2]).to have_content('issue_1')
+ expect(results_list.all('.todo-title')[3]).to have_content('issue_2')
+ expect(results_list.all('.todo-title')[4]).to have_content('issue_4')
+ end
end
context 'issues and merge requests' do
diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb
index 68d979bb1cf..04e78b59ab4 100644
--- a/spec/features/dashboard/todos/todos_spec.rb
+++ b/spec/features/dashboard/todos/todos_spec.rb
@@ -211,9 +211,9 @@ RSpec.describe 'Dashboard Todos' do
visit dashboard_todos_path
end
- it 'shows you directly addressed yourself message' do
+ it 'shows you directly addressed yourself message being displayed as mentioned yourself' do
page.within('.js-todos-all') do
- expect(page).to have_content("You directly addressed yourself on issue #{issue.to_reference} \"Fix bug\" at #{project.namespace.owner_name} / #{project.name}")
+ expect(page).to have_content("You mentioned yourself on issue #{issue.to_reference} \"Fix bug\" at #{project.namespace.owner_name} / #{project.name}")
expect(page).not_to have_content('to yourself')
end
end
diff --git a/spec/features/explore/topics_spec.rb b/spec/features/explore/topics_spec.rb
index d6f3d6a123d..f0c57c2417a 100644
--- a/spec/features/explore/topics_spec.rb
+++ b/spec/features/explore/topics_spec.rb
@@ -13,13 +13,13 @@ RSpec.describe 'Explore Topics' do
end
context 'when topics exist' do
- let!(:topic) { create(:topic, name: 'topic1') }
+ let!(:topic) { create(:topic, name: 'topic1', title: 'Topic 1') }
it 'renders topic list' do
visit topics_explore_projects_path
expect(page).to have_current_path topics_explore_projects_path, ignore_query: true
- expect(page).to have_content('topic1')
+ expect(page).to have_content(topic.title)
end
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 6bc3b745851..7fbbc4dfc85 100644
--- a/spec/features/frequently_visited_projects_and_groups_spec.rb
+++ b/spec/features/frequently_visited_projects_and_groups_spec.rb
@@ -16,7 +16,6 @@ RSpec.describe 'Frequently visited items', :js do
it 'increments localStorage counter when visiting the project' do
visit project_path(project)
- open_top_nav_projects
frequent_projects = nil
@@ -35,7 +34,6 @@ RSpec.describe 'Frequently visited items', :js do
it 'increments localStorage counter when visiting the group' do
visit group_path(group)
- open_top_nav_groups
frequent_groups = nil
diff --git a/spec/features/groups/clusters/eks_spec.rb b/spec/features/groups/clusters/eks_spec.rb
deleted file mode 100644
index 0e64a2faf3e..00000000000
--- a/spec/features/groups/clusters/eks_spec.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Group AWS EKS Cluster', :js do
- let(:group) { create(:group) }
- let(:user) { create(:user) }
-
- before do
- group.add_maintainer(user)
- gitlab_sign_in(user)
-
- allow(Groups::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
- allow_any_instance_of(Clusters::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute)
- allow_any_instance_of(Clusters::Cluster).to receive(:retrieve_connection_status).and_return(:connected)
- stub_application_setting(eks_integration_enabled: true)
- end
-
- context 'when user does not have a cluster and visits group clusters page' do
- before do
- visit group_clusters_path(group)
-
- click_button(class: 'dropdown-toggle-split')
- click_link 'Create a cluster (deprecated)'
- end
-
- context 'when user creates a cluster on AWS EKS' do
- before do
- click_link 'Amazon EKS'
- end
-
- it 'user sees a form to create an EKS cluster' do
- expect(page).to have_content('Authenticate with Amazon Web Services')
- end
- end
- end
-end
diff --git a/spec/features/groups/crm/contacts/create_spec.rb b/spec/features/groups/crm/contacts/create_spec.rb
new file mode 100644
index 00000000000..d6c6e3f1745
--- /dev/null
+++ b/spec/features/groups/crm/contacts/create_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Create a CRM contact', :js do
+ let(:user) { create(:user) }
+ let(:group) { create(:group, :crm_enabled) }
+ let!(:organization) { create(:organization, group: group, name: 'GitLab') }
+
+ before do
+ group.add_owner(user)
+ sign_in(user)
+ visit new_group_crm_contact_path(group)
+ end
+
+ it 'creates a new contact' do
+ fill_in 'firstName', with: 'Forename'
+ fill_in 'lastName', with: 'Surname'
+ fill_in 'email', with: 'gitlab@example.com'
+ fill_in 'phone', with: '01234 555666'
+ select 'GitLab', from: 'organizationId'
+ fill_in 'description', with: 'VIP'
+ click_button 'Save changes'
+
+ expect(page).to have_content 'gitlab@example.com'
+ expect(page).to have_current_path("#{group_crm_contacts_path(group)}/", ignore_query: true)
+ end
+end
diff --git a/spec/features/groups/dependency_proxy_spec.rb b/spec/features/groups/dependency_proxy_spec.rb
index 623fb065bfc..af9c4a40729 100644
--- a/spec/features/groups/dependency_proxy_spec.rb
+++ b/spec/features/groups/dependency_proxy_spec.rb
@@ -88,22 +88,6 @@ RSpec.describe 'Group Dependency Proxy' do
sign_in(owner)
end
- context 'feature flag is disabled', :js do
- before do
- stub_feature_flags(dependency_proxy_for_private_groups: false)
- end
-
- context 'group is private' do
- let(:group) { create(:group, :private) }
-
- it 'informs user that feature is only available for public groups' do
- visit path
-
- expect(page).to have_content('Dependency Proxy feature is limited to public groups for now.')
- end
- end
- end
-
context 'feature is disabled globally' do
it 'renders 404 page' do
disable_feature
diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb
index 0317f9162cc..71f38401fa1 100644
--- a/spec/features/groups/empty_states_spec.rb
+++ b/spec/features/groups/empty_states_spec.rb
@@ -7,6 +7,8 @@ RSpec.describe 'Group empty states' do
let(:user) { create(:group_member, :developer, user: create(:user), group: group ).user }
before do
+ stub_feature_flags(vue_issues_list: true)
+
sign_in(user)
end
@@ -100,21 +102,23 @@ RSpec.describe 'Group empty states' do
end
it "the new #{issuable_name} button opens a project dropdown" do
- within '.empty-state' do
- click_button 'Toggle project select'
- end
+ click_button 'Toggle project select'
- expect(page).to have_selector('.ajax-project-dropdown')
+ if issuable == :issue
+ expect(page).to have_button project.name
+ else
+ expect(page).to have_selector('.ajax-project-dropdown')
+ end
end
end
end
shared_examples "no projects" do
- it 'displays an empty state' do
+ it 'displays an empty state', :js do
expect(page).to have_selector('.empty-state')
end
- it "does not show a new #{issuable_name} button" do
+ it "does not show a new #{issuable_name} button", :js do
within '.empty-state' do
expect(page).not_to have_link("create #{issuable_name}")
end
@@ -143,7 +147,7 @@ RSpec.describe 'Group empty states' do
visit path
end
- it 'displays an empty state' do
+ it 'displays an empty state', :js do
expect(page).to have_selector('.empty-state')
end
end
diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb
index 50982cb1452..019b094ccb5 100644
--- a/spec/features/groups/group_settings_spec.rb
+++ b/spec/features/groups/group_settings_spec.rb
@@ -223,7 +223,7 @@ RSpec.describe 'Edit group settings' do
check 'group_prevent_sharing_groups_outside_hierarchy'
expect { save_permissions_group }.to change {
- group.reload.namespace_settings.prevent_sharing_groups_outside_hierarchy
+ group.reload.prevent_sharing_groups_outside_hierarchy
}.to(true)
end
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index 6b663445124..ef3346b9763 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -11,6 +11,10 @@ RSpec.describe 'Group issues page' do
let(:project_with_issues_disabled) { create(:project, :issues_disabled, group: group) }
let(:path) { issues_group_path(group) }
+ before do
+ stub_feature_flags(vue_issues_list: true)
+ end
+
context 'with shared examples', :js do
let(:issuable) { create(:issue, project: project, title: "this is my created issuable")}
@@ -58,10 +62,10 @@ RSpec.describe 'Group issues page' do
let(:user2) { user_outside_group }
it 'filters by only group users' do
- filtered_search.set('assignee:=')
+ select_tokens 'Assignee', '='
- expect(find('#js-dropdown-assignee .filter-dropdown')).to have_content(user.name)
- expect(find('#js-dropdown-assignee .filter-dropdown')).not_to have_content(user2.name)
+ expect_suggestion(user.name)
+ expect_no_suggestion(user2.name)
end
end
end
@@ -76,23 +80,9 @@ RSpec.describe 'Group issues page' do
it 'returns all group and subgroup issues' do
visit issues_group_path(group)
- page.within('.issuable-list') do
- expect(page).to have_selector('li.issue', count: 2)
- expect(page).to have_content('root group issue')
- expect(page).to have_content('subgroup issue')
- end
- end
-
- it 'truncates issue counts if over the threshold', :clean_gitlab_redis_cache do
- allow(Rails.cache).to receive(:read).and_call_original
- allow(Rails.cache).to receive(:read).with(
- ['group', group.id, 'issues'],
- { expires_in: Gitlab::IssuablesCountForState::CACHE_EXPIRES_IN }
- ).and_return({ opened: 1050, closed: 500, all: 1550 })
-
- visit issues_group_path(group)
-
- expect(page).to have_text('Open 1.1k Closed 500 All 1.6k')
+ expect(page).to have_selector('li.issue', count: 2)
+ expect(page).to have_content('root group issue')
+ expect(page).to have_content('subgroup issue')
end
context 'when project is archived' do
@@ -115,7 +105,6 @@ RSpec.describe 'Group issues page' do
let!(:subgroup_issue) { create(:issue, project: subgroup_project) }
before do
- stub_feature_flags(vue_issues_list: true)
visit issues_group_path(group_with_no_issues)
end
@@ -135,14 +124,10 @@ RSpec.describe 'Group issues page' do
end
it 'shows projects only with issues feature enabled', :js do
- within '.empty-state' do
- click_button 'Toggle project select'
- end
+ click_button 'Toggle project select'
- page.within('.select2-results') do
- expect(page).to have_content(project.full_name)
- expect(page).not_to have_content(project_with_issues_disabled.full_name)
- end
+ expect(page).to have_button project.full_name
+ expect(page).not_to have_button project_with_issues_disabled.full_name
end
end
end
@@ -155,15 +140,15 @@ RSpec.describe 'Group issues page' do
let!(:issue3) { create(:issue, project: project, title: 'Issue #3', relative_position: 3) }
before do
+ stub_feature_flags(vue_issues_list: false)
+
sign_in(user_in_group)
end
it 'displays all issues' do
visit issues_group_path(group, sort: 'relative_position')
- page.within('.issues-list') do
- expect(page).to have_selector('li.issue', count: 3)
- end
+ expect(page).to have_selector('li.issue', count: 3)
end
it 'has manual-ordering css applied' do
@@ -218,11 +203,9 @@ RSpec.describe 'Group issues page' do
end
def check_issue_order
- page.within('.manual-ordering') do
- expect(find('.issue:nth-child(1) .title')).to have_content('Issue #2')
- expect(find('.issue:nth-child(2) .title')).to have_content('Issue #3')
- expect(find('.issue:nth-child(3) .title')).to have_content('Issue #1')
- end
+ expect(page).to have_css('.issue:nth-child(1) .title', text: 'Issue #2')
+ expect(page).to have_css('.issue:nth-child(2) .title', text: 'Issue #3')
+ expect(page).to have_css('.issue:nth-child(3) .title', text: 'Issue #1')
end
end
@@ -239,14 +222,8 @@ RSpec.describe 'Group issues page' do
end
it 'shows the pagination' do
- expect(page).to have_link 'Prev'
- expect(page).to have_link 'Next'
- end
-
- it 'first pagination item is active' do
- page.within('.gl-pagination') do
- expect(find('li.active')).to have_content('1')
- end
+ expect(page).to have_button 'Prev', disabled: true
+ expect(page).to have_button 'Next'
end
end
end
diff --git a/spec/features/groups/members/manage_groups_spec.rb b/spec/features/groups/members/manage_groups_spec.rb
index 5a9223d9ee8..e4252e2f3aa 100644
--- a/spec/features/groups/members/manage_groups_spec.rb
+++ b/spec/features/groups/members/manage_groups_spec.rb
@@ -119,141 +119,11 @@ RSpec.describe 'Groups > Members > Manage groups', :js do
describe 'group search results' do
let_it_be(:group, refind: true) { create(:group) }
- context 'with instance admin considerations' do
- let_it_be(:group_to_share) { create(:group) }
-
- context 'when user is an admin' do
- let_it_be(:admin) { create(:admin) }
-
- before do
- sign_in(admin)
- gitlab_enable_admin_mode_sign_in(admin)
- end
-
- it 'shows groups where the admin has no direct membership' do
- visit group_group_members_path(group)
-
- click_on 'Invite a group'
- click_on 'Select a group'
- wait_for_requests
-
- page.within(group_dropdown_selector) do
- expect_to_have_group(group_to_share)
- expect_not_to_have_group(group)
- end
- end
-
- it 'shows groups where the admin has at least guest level membership' do
- group_to_share.add_guest(admin)
-
- visit group_group_members_path(group)
-
- click_on 'Invite a group'
- click_on 'Select a group'
- wait_for_requests
-
- page.within(group_dropdown_selector) do
- expect_to_have_group(group_to_share)
- expect_not_to_have_group(group)
- end
- end
- end
-
- context 'when user is not an admin' do
- before do
- group.add_owner(user)
- end
-
- it 'shows groups where the user has no direct membership' do
- visit group_group_members_path(group)
-
- click_on 'Invite a group'
- click_on 'Select a group'
- wait_for_requests
-
- page.within(group_dropdown_selector) do
- expect_not_to_have_group(group_to_share)
- expect_not_to_have_group(group)
- end
- end
-
- it 'shows groups where the user has at least guest level membership' do
- group_to_share.add_guest(user)
-
- visit group_group_members_path(group)
-
- click_on 'Invite a group'
- click_on 'Select a group'
- wait_for_requests
-
- page.within(group_dropdown_selector) do
- expect_to_have_group(group_to_share)
- expect_not_to_have_group(group)
- end
- end
- end
- end
-
- context 'when user is not an admin and there are hierarchy considerations' do
+ it_behaves_like 'inviting groups search results' do
+ let_it_be(:entity) { group }
let_it_be(:group_within_hierarchy) { create(:group, parent: group) }
- let_it_be(:group_outside_hierarchy) { create(:group) }
-
- before_all do
- group.add_owner(user)
- group_within_hierarchy.add_owner(user)
- group_outside_hierarchy.add_owner(user)
- end
-
- it 'does not show self or ancestors', :aggregate_failures do
- group_sibbling = create(:group, parent: group)
- group_sibbling.add_owner(user)
-
- visit group_group_members_path(group_within_hierarchy)
-
- click_on 'Invite a group'
- click_on 'Select a group'
- wait_for_requests
-
- page.within(group_dropdown_selector) do
- expect_to_have_group(group_outside_hierarchy)
- expect_to_have_group(group_sibbling)
- expect_not_to_have_group(group)
- expect_not_to_have_group(group_within_hierarchy)
- end
- end
-
- context 'when sharing with groups outside the hierarchy is enabled' do
- it 'shows groups within and outside the hierarchy in search results' do
- visit group_group_members_path(group)
-
- click_on 'Invite a group'
- click_on 'Select a group'
- wait_for_requests
-
- page.within(group_dropdown_selector) do
- expect_to_have_group(group_within_hierarchy)
- expect_to_have_group(group_outside_hierarchy)
- end
- end
- end
-
- context 'when sharing with groups outside the hierarchy is disabled' do
- before do
- group.namespace_settings.update!(prevent_sharing_groups_outside_hierarchy: true)
- end
-
- it 'shows only groups within the hierarchy in search results' do
- visit group_group_members_path(group)
-
- click_on 'Invite a group'
- click_on 'Select a group'
-
- page.within(group_dropdown_selector) do
- expect_to_have_group(group_within_hierarchy)
- expect_not_to_have_group(group_outside_hierarchy)
- end
- end
- end
+ let_it_be(:members_page_path) { group_group_members_path(entity) }
+ let_it_be(:members_page_path_within_hierarchy) { group_group_members_path(group_within_hierarchy) }
end
end
end
diff --git a/spec/features/groups/settings/ci_cd_spec.rb b/spec/features/groups/settings/ci_cd_spec.rb
index c5ad524e647..50c481c115c 100644
--- a/spec/features/groups/settings/ci_cd_spec.rb
+++ b/spec/features/groups/settings/ci_cd_spec.rb
@@ -17,62 +17,29 @@ RSpec.describe 'Group CI/CD settings' do
end
describe 'Runners section' do
- let(:shared_runners_toggle) { page.find('[data-testid="enable-runners-toggle"]') }
+ let(:shared_runners_toggle) { page.find('[data-testid="shared-runners-toggle"]') }
- context 'with runner_list_group_view_vue_ui enabled' do
- before do
- visit group_settings_ci_cd_path(group)
- end
-
- it 'displays the new group runners view banner' do
- expect(page).to have_content(s_('Runners|New group runners view'))
- expect(page).to have_link(href: group_runners_path(group))
- end
-
- it 'has "Enable shared runners for this group" toggle', :js do
- expect(shared_runners_toggle).to have_content(_('Enable shared runners for this group'))
- end
+ before do
+ visit group_settings_ci_cd_path(group)
end
- context 'with runner_list_group_view_vue_ui disabled' do
- before do
- stub_feature_flags(runner_list_group_view_vue_ui: false)
-
- visit group_settings_ci_cd_path(group)
- end
-
- it 'does not display the new group runners view banner' do
- expect(page).not_to have_content(s_('Runners|New group runners view'))
- expect(page).not_to have_link(href: group_runners_path(group))
- end
-
- it 'has "Enable shared runners for this group" toggle', :js do
- expect(shared_runners_toggle).to have_content(_('Enable shared runners for this group'))
- end
-
- context 'with runners registration token' do
- let!(:token) { group.runners_token }
-
- before do
- visit group_settings_ci_cd_path(group)
- end
+ it 'displays the new group runners view banner' do
+ expect(page).to have_content(s_('Runners|New group runners view'))
+ expect(page).to have_link(href: group_runners_path(group))
+ end
- it 'displays the registration token' do
- expect(page.find('#registration_token')).to have_content(token)
- end
+ it 'has "Enable shared runners for this group" toggle', :js do
+ expect(shared_runners_toggle).to have_content(_('Enable shared runners for this group'))
+ end
- describe 'reload registration token' do
- let(:page_token) { find('#registration_token').text }
+ it 'clicks on toggle to enable setting', :js do
+ expect(group.shared_runners_setting).to be(Namespace::SR_ENABLED)
- before do
- click_button 'Reset registration token'
- end
+ shared_runners_toggle.find('button').click
+ wait_for_requests
- it 'changes the registration token' do
- expect(page_token).not_to eq token
- end
- end
- end
+ group.reload
+ expect(group.shared_runners_setting).to be(Namespace::SR_DISABLED_AND_UNOVERRIDABLE)
end
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 08183badda1..ceb4af03f89 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -105,6 +105,24 @@ RSpec.describe 'Group' do
expect(page).to have_content('Group path is available')
end
+
+ context 'when filling in the `Group name` field' do
+ let_it_be(:group1) { create(:group, :public, path: 'foo-bar') }
+ let_it_be(:group2) { create(:group, :public, path: 'bar-baz') }
+
+ it 'automatically populates the `Group URL` field' do
+ fill_in 'Group name', with: 'Foo bar'
+ # Wait for debounce in app/assets/javascripts/group.js#18
+ sleep(1)
+ fill_in 'Group name', with: 'Bar baz'
+ # Wait for debounce in app/assets/javascripts/group.js#18
+ sleep(1)
+
+ wait_for_requests
+
+ expect(page).to have_field('Group URL', with: 'bar-baz1')
+ end
+ end
end
describe 'Mattermost team creation' do
diff --git a/spec/features/ide/user_opens_merge_request_spec.rb b/spec/features/ide/user_opens_merge_request_spec.rb
index 72fe6eb6ca8..8f4668d49ee 100644
--- a/spec/features/ide/user_opens_merge_request_spec.rb
+++ b/spec/features/ide/user_opens_merge_request_spec.rb
@@ -14,6 +14,7 @@ RSpec.describe 'IDE merge request', :js do
end
it 'user opens merge request' do
+ click_button 'Code'
click_link 'Open in Web IDE'
wait_for_requests
diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb
index a0786d36fdf..7edf5fdc5ff 100644
--- a/spec/features/issuables/issuable_list_spec.rb
+++ b/spec/features/issuables/issuable_list_spec.rb
@@ -9,6 +9,8 @@ RSpec.describe 'issuable list', :js do
issuable_types = [:issue, :merge_request]
before do
+ stub_feature_flags(vue_issues_list: true)
+
project.add_user(user, :developer)
sign_in(user)
issuable_types.each { |type| create_issuables(type) }
@@ -34,16 +36,16 @@ RSpec.describe 'issuable list', :js do
it 'sorts labels alphabetically' do
label1 = create(:label, project: project, title: 'a')
label2 = create(:label, project: project, title: 'z')
- label3 = create(:label, project: project, title: 'X')
- label4 = create(:label, project: project, title: 'B')
+ label3 = create(:label, project: project, title: 'x')
+ label4 = create(:label, project: project, title: 'b')
issuable = create_issuable(issuable_type)
issuable.labels << [label1, label2, label3, label4]
visit_issuable_list(issuable_type)
- expect(all('.gl-label-text')[0].text).to have_content('B')
- expect(all('.gl-label-text')[1].text).to have_content('X')
- expect(all('.gl-label-text')[2].text).to have_content('a')
+ expect(all('.gl-label-text')[0].text).to have_content('a')
+ expect(all('.gl-label-text')[1].text).to have_content('b')
+ expect(all('.gl-label-text')[2].text).to have_content('x')
expect(all('.gl-label-text')[3].text).to have_content('z')
end
end
diff --git a/spec/features/issuables/sorting_list_spec.rb b/spec/features/issuables/sorting_list_spec.rb
index bc40fb713ac..53723b39d5b 100644
--- a/spec/features/issuables/sorting_list_spec.rb
+++ b/spec/features/issuables/sorting_list_spec.rb
@@ -88,14 +88,14 @@ RSpec.describe 'Sort Issuable List' do
end
end
- context 'custom sorting' do
+ context 'custom sorting', :js do
let(:issuable_type) { :merge_request }
it 'supports sorting in asc and desc order' do
visit_merge_requests_with_state(project, 'open')
click_button('Created date')
- click_link('Updated date')
+ find('.dropdown-item', text: 'Updated date').click
expect(first_merge_request).to include(last_updated_issuable.title)
expect(last_merge_request).to include(first_updated_issuable.title)
diff --git a/spec/features/issue_rebalancing_spec.rb b/spec/features/issue_rebalancing_spec.rb
index 978768270ec..8a05aeec7ec 100644
--- a/spec/features/issue_rebalancing_spec.rb
+++ b/spec/features/issue_rebalancing_spec.rb
@@ -15,6 +15,10 @@ RSpec.describe 'Issue rebalancing' do
group.add_developer(user)
end
+ before do
+ stub_feature_flags(vue_issues_list: true)
+ end
+
context 'when issue rebalancing is in progress' do
before do
sign_in(user)
@@ -38,16 +42,16 @@ RSpec.describe 'Issue rebalancing' do
expect(page).to have_selector('.gl-alert-info', text: alert_message_regex, count: 1)
end
- it 'shows an alert in project issues list with manual sort' do
+ it 'shows an alert in project issues list with manual sort', :js do
visit project_issues_path(project, sort: 'relative_position')
- expect(page).to have_selector('.gl-alert-info', text: alert_message_regex, count: 1)
+ expect(page).to have_selector('.flash-notice', text: alert_message_regex, count: 1)
end
- it 'shows an alert in group issues list with manual sort' do
+ it 'shows an alert in group issues list with manual sort', :js do
visit issues_group_path(group, sort: 'relative_position')
- expect(page).to have_selector('.gl-alert-info', text: alert_message_regex, count: 1)
+ expect(page).to have_selector('.flash-notice', text: alert_message_regex, count: 1)
end
it 'does not show an alert in project issues list with other sorts' do
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 507d427bf0b..1e8b9b6b60b 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
@@ -26,7 +26,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
end
it 'shows a button to resolve all threads by creating a new issue' do
- within('.line-resolve-all-container') do
+ within('.discussions-counter') do
expect(page).to have_selector resolve_all_discussions_link_selector( title: "Create issue to resolve all threads" )
end
end
@@ -76,14 +76,12 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
end
it 'has a link to resolve all threads by creating an issue' do
- page.within '.mr-widget-body' do
- expect(page).to have_link 'Create issue to resolve all threads', href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid)
- end
+ expect(page).to have_link 'Create issue to resolve all threads', href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid)
end
context 'creating an issue for threads' do
before do
- page.within '.mr-widget-body' do
+ page.within '.mr-state-widget' do
page.click_link 'Create issue to resolve all threads', href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid)
wait_for_all_requests
diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
index 3ba2f7e788d..18b70c9622a 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -6,11 +6,12 @@ RSpec.describe 'Dropdown assignee', :js do
include FilteredSearchHelpers
let_it_be(:project) { create(:project) }
- let_it_be(:user) { create(:user, name: 'administrator', username: 'root') }
+ let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) }
- let(:js_dropdown_assignee) { '#js-dropdown-assignee' }
- let(:filter_dropdown) { find("#{js_dropdown_assignee} .filter-dropdown") }
+ before do
+ stub_feature_flags(vue_issues_list: true)
+ end
describe 'behavior' do
before do
@@ -21,15 +22,17 @@ RSpec.describe 'Dropdown assignee', :js do
end
it 'loads all the assignees when opened' do
- input_filtered_search('assignee:=', submit: false, extra_space: false)
+ select_tokens 'Assignee', '='
- expect_filtered_search_dropdown_results(filter_dropdown, 2)
+ # Expect None, Any, administrator, John Doe2
+ expect_suggestion_count 4
end
it 'shows current user at top of dropdown' do
- input_filtered_search('assignee:=', submit: false, extra_space: false)
+ select_tokens 'Assignee', '='
- expect(filter_dropdown.first('.filter-dropdown-item')).to have_content(user.name)
+ # List items 1 to 3 are None, Any, divider
+ expect(page).to have_css('.gl-filtered-search-suggestion:nth-child(4)', text: user.name)
end
end
@@ -41,7 +44,7 @@ RSpec.describe 'Dropdown assignee', :js do
visit project_issues_path(project)
Gitlab::Testing::RequestBlockerMiddleware.block_requests!
- input_filtered_search('assignee:=', submit: false, extra_space: false)
+ select_tokens 'Assignee', '='
end
after do
@@ -49,11 +52,10 @@ RSpec.describe 'Dropdown assignee', :js do
end
it 'selects current user' do
- find("#{js_dropdown_assignee} .filter-dropdown-item", text: user.username).click
+ click_on user.username
- expect(page).to have_css(js_dropdown_assignee, visible: false)
- expect_tokens([assignee_token(user.username)])
- expect_filtered_search_input_empty
+ expect_assignee_token(user.username)
+ expect_empty_search_term
end
end
@@ -93,7 +95,7 @@ RSpec.describe 'Dropdown assignee', :js do
it 'shows inherited, direct, and invited group members but not descendent members', :aggregate_failures do
visit issues_group_path(subgroup)
- input_filtered_search('assignee:=', submit: false, extra_space: false)
+ select_tokens 'Assignee', '='
expect(page).to have_text group_user.name
expect(page).to have_text subgroup_user.name
@@ -103,7 +105,7 @@ RSpec.describe 'Dropdown assignee', :js do
visit project_issues_path(subgroup_project)
- input_filtered_search('assignee:=', submit: false, extra_space: false)
+ select_tokens 'Assignee', '='
expect(page).to have_text group_user.name
expect(page).to have_text subgroup_user.name
diff --git a/spec/features/issues/filtered_search/dropdown_author_spec.rb b/spec/features/issues/filtered_search/dropdown_author_spec.rb
index 893ffc6575b..07e2bd3b7e4 100644
--- a/spec/features/issues/filtered_search/dropdown_author_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_author_spec.rb
@@ -6,13 +6,12 @@ RSpec.describe 'Dropdown author', :js do
include FilteredSearchHelpers
let_it_be(:project) { create(:project) }
- let_it_be(:user) { create(:user, name: 'administrator', username: 'root') }
+ let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) }
- let(:js_dropdown_author) { '#js-dropdown-author' }
- let(:filter_dropdown) { find("#{js_dropdown_author} .filter-dropdown") }
-
before do
+ stub_feature_flags(vue_issues_list: true)
+
project.add_maintainer(user)
sign_in(user)
@@ -21,22 +20,22 @@ RSpec.describe 'Dropdown author', :js do
describe 'behavior' do
it 'loads all the authors when opened' do
- input_filtered_search('author:=', submit: false, extra_space: false)
+ select_tokens 'Author', '='
- expect_filtered_search_dropdown_results(filter_dropdown, 2)
+ expect_suggestion_count 2
end
it 'shows current user at top of dropdown' do
- input_filtered_search('author:=', submit: false, extra_space: false)
+ select_tokens 'Author', '='
- expect(filter_dropdown.first('.filter-dropdown-item')).to have_content(user.name)
+ expect(page).to have_css('.gl-filtered-search-suggestion:first-child', text: user.name)
end
end
describe 'selecting from dropdown without Ajax call' do
before do
Gitlab::Testing::RequestBlockerMiddleware.block_requests!
- input_filtered_search('author:=', submit: false, extra_space: false)
+ select_tokens 'Author', '='
end
after do
@@ -44,11 +43,10 @@ RSpec.describe 'Dropdown author', :js do
end
it 'selects current user' do
- find("#{js_dropdown_author} .filter-dropdown-item", text: user.username).click
+ click_on user.username
- expect(page).to have_css(js_dropdown_author, visible: false)
- expect_tokens([author_token(user.username)])
- expect_filtered_search_input_empty
+ expect_author_token(user.username)
+ expect_empty_search_term
end
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_base_spec.rb b/spec/features/issues/filtered_search/dropdown_base_spec.rb
index b8fb807dd78..5fdab288b2d 100644
--- a/spec/features/issues/filtered_search/dropdown_base_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_base_spec.rb
@@ -6,18 +6,12 @@ RSpec.describe 'Dropdown base', :js do
include FilteredSearchHelpers
let_it_be(:project) { create(:project) }
- let_it_be(:user) { create(:user, name: 'administrator', username: 'root') }
+ let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) }
- let(:filtered_search) { find('.filtered-search') }
- let(:js_dropdown_assignee) { '#js-dropdown-assignee' }
- let(:filter_dropdown) { find("#{js_dropdown_assignee} .filter-dropdown") }
-
- def dropdown_assignee_size
- filter_dropdown.all('.filter-dropdown-item').size
- end
-
before do
+ stub_feature_flags(vue_issues_list: true)
+
project.add_maintainer(user)
sign_in(user)
@@ -26,17 +20,17 @@ RSpec.describe 'Dropdown base', :js do
describe 'caching requests' do
it 'caches requests after the first load' do
- input_filtered_search('assignee:=', submit: false, extra_space: false)
- initial_size = dropdown_assignee_size
+ select_tokens 'Assignee', '='
+ initial_size = get_suggestion_count
expect(initial_size).to be > 0
new_user = create(:user)
project.add_maintainer(new_user)
- find('.filtered-search-box .clear-search').click
- input_filtered_search('assignee:=', submit: false, extra_space: false)
+ click_button 'Clear'
+ select_tokens 'Assignee', '='
- expect(dropdown_assignee_size).to eq(initial_size)
+ expect_suggestion_count(initial_size)
end
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
index f5ab53d5052..d6d59b89a8c 100644
--- a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
@@ -6,15 +6,13 @@ RSpec.describe 'Dropdown emoji', :js do
include FilteredSearchHelpers
let_it_be(:project) { create(:project, :public) }
- let_it_be(:user) { create(:user, name: 'administrator', username: 'root') }
+ let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:award_emoji_star) { create(:award_emoji, name: 'star', user: user, awardable: issue) }
- let(:filtered_search) { find('.filtered-search') }
- let(:js_dropdown_emoji) { '#js-dropdown-my-reaction' }
- let(:filter_dropdown) { find("#{js_dropdown_emoji} .filter-dropdown") }
-
before do
+ stub_feature_flags(vue_issues_list: true)
+
project.add_maintainer(user)
create_list(:award_emoji, 2, user: user, name: 'thumbsup')
create_list(:award_emoji, 1, user: user, name: 'thumbsdown')
@@ -27,15 +25,15 @@ RSpec.describe 'Dropdown emoji', :js do
end
describe 'behavior' do
- it 'does not open when the search bar has my-reaction=' do
- filtered_search.set('my-reaction=')
+ it 'does not contain My-Reaction in the list of suggestions' do
+ click_filtered_search_bar
- expect(page).not_to have_css(js_dropdown_emoji)
+ expect(page).not_to have_link 'My-Reaction'
end
end
end
- context 'when user loggged in' do
+ context 'when user logged in' do
before do
sign_in(user)
@@ -43,22 +41,18 @@ RSpec.describe 'Dropdown emoji', :js do
end
describe 'behavior' do
- it 'opens when the search bar has my-reaction=' do
- filtered_search.set('my-reaction:=')
-
- expect(page).to have_css(js_dropdown_emoji, visible: true)
- end
-
it 'loads all the emojis when opened' do
- input_filtered_search('my-reaction:=', submit: false, extra_space: false)
+ select_tokens 'My-Reaction', '='
- expect_filtered_search_dropdown_results(filter_dropdown, 3)
+ # Expect None, Any, star, thumbsup, thumbsdown
+ expect_suggestion_count 5
end
it 'shows the most populated emoji at top of dropdown' do
- input_filtered_search('my-reaction:=', submit: false, extra_space: false)
+ select_tokens 'My-Reaction', '='
- expect(first("#{js_dropdown_emoji} .filter-dropdown li")).to have_content(award_emoji_star.name)
+ # List items 1-3 are None, Any, divider
+ expect(page).to have_css('.gl-filtered-search-suggestion-list li:nth-child(4)', text: award_emoji_star.name)
end
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
index 9cc58a33bb7..c64247b2b15 100644
--- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
@@ -9,19 +9,9 @@ RSpec.describe 'Dropdown hint', :js do
let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) }
- let(:filtered_search) { find('.filtered-search') }
- let(:js_dropdown_hint) { '#js-dropdown-hint' }
- let(:js_dropdown_operator) { '#js-dropdown-operator' }
-
- def click_hint(text)
- find('#js-dropdown-hint .filter-dropdown .filter-dropdown-item', text: text).click
- end
-
- def click_operator(op)
- find("#js-dropdown-operator .filter-dropdown .filter-dropdown-item[data-value='#{op}']").click
- end
-
before do
+ stub_feature_flags(vue_issues_list: true)
+
project.add_maintainer(user)
end
@@ -31,8 +21,9 @@ RSpec.describe 'Dropdown hint', :js do
end
it 'does not exist my-reaction dropdown item' do
- expect(page).to have_css(js_dropdown_hint, visible: false)
- expect(page).not_to have_content('My-reaction')
+ click_filtered_search_bar
+
+ expect(page).not_to have_link 'My-reaction'
end
end
@@ -45,57 +36,56 @@ RSpec.describe 'Dropdown hint', :js do
describe 'behavior' do
before do
- expect(page).to have_css(js_dropdown_hint, visible: false)
- filtered_search.click
+ click_filtered_search_bar
end
it 'opens when the search bar is first focused' do
- expect(page).to have_css(js_dropdown_hint, visible: true)
+ expect_visible_suggestions_list
find('body').click
- expect(page).to have_css(js_dropdown_hint, visible: false)
+ expect_hidden_suggestions_list
end
end
describe 'filtering' do
it 'filters with text' do
- filtered_search.set('a')
+ click_filtered_search_bar
+ send_keys 'as'
- expect(find(js_dropdown_hint)).to have_selector('.filter-dropdown .filter-dropdown-item', count: 6)
+ # Expect Assignee and Release
+ expect_suggestion_count 2
end
end
describe 'selecting from dropdown with no input' do
before do
- filtered_search.click
+ click_filtered_search_bar
end
it 'opens the token dropdown when you click on it' do
- click_hint('Author')
+ click_link 'Author'
- expect(page).to have_css(js_dropdown_hint, visible: false)
- expect(page).to have_css(js_dropdown_operator, visible: true)
+ expect_visible_suggestions_list
+ expect_suggestion '='
- click_operator('=')
+ click_link '= is'
- expect(page).to have_css(js_dropdown_hint, visible: false)
- expect(page).to have_css(js_dropdown_operator, visible: false)
- expect(page).to have_css('#js-dropdown-author', visible: true)
- expect_tokens([{ name: 'Author', operator: '=' }])
- expect_filtered_search_input_empty
+ expect_visible_suggestions_list
+ expect_token_segment 'Author'
+ expect_token_segment '='
+ expect_empty_search_term
end
end
describe 'reselecting from dropdown' do
it 'reuses existing token text' do
- filtered_search.send_keys('author')
- filtered_search.send_keys(:backspace)
- filtered_search.send_keys(:backspace)
- click_hint('Author')
+ click_filtered_search_bar
+ send_keys 'author', :backspace, :backspace
+ click_link 'Author'
- expect_tokens([{ name: 'Author' }])
- expect_filtered_search_input_empty
+ expect_token_segment 'Author'
+ expect_empty_search_term
end
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_label_spec.rb b/spec/features/issues/filtered_search/dropdown_label_spec.rb
index 1b48810f716..67e3792a04c 100644
--- a/spec/features/issues/filtered_search/dropdown_label_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_label_spec.rb
@@ -10,10 +10,9 @@ RSpec.describe 'Dropdown label', :js do
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:label) { create(:label, project: project, title: 'bug-label') }
- let(:filtered_search) { find('.filtered-search') }
- let(:filter_dropdown) { find('#js-dropdown-label .filter-dropdown') }
-
before do
+ stub_feature_flags(vue_issues_list: true)
+
project.add_maintainer(user)
sign_in(user)
@@ -22,9 +21,10 @@ RSpec.describe 'Dropdown label', :js do
describe 'behavior' do
it 'loads all the labels when opened' do
- filtered_search.set('label:=')
+ select_tokens 'Label', '='
- expect_filtered_search_dropdown_results(filter_dropdown, 1)
+ # Expect None, Any, bug-label
+ expect_suggestion_count 3
end
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
index 859d1e4a5e5..19a4c8853f1 100644
--- a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
@@ -11,10 +11,9 @@ RSpec.describe 'Dropdown milestone', :js do
let_it_be(:uppercase_milestone) { create(:milestone, title: 'CAP_MILESTONE', project: project) }
let_it_be(:issue) { create(:issue, project: project) }
- let(:filtered_search) { find('.filtered-search') }
- let(:filter_dropdown) { find('#js-dropdown-milestone .filter-dropdown') }
-
before do
+ stub_feature_flags(vue_issues_list: true)
+
project.add_maintainer(user)
sign_in(user)
@@ -22,12 +21,11 @@ RSpec.describe 'Dropdown milestone', :js do
end
describe 'behavior' do
- before do
- filtered_search.set('milestone:=')
- end
-
it 'loads all the milestones when opened' do
- expect_filtered_search_dropdown_results(filter_dropdown, 2)
+ select_tokens 'Milestone', '='
+
+ # Expect None, Any, Upcoming, Started, CAP_MILESTONE, v1.0
+ expect_suggestion_count 6
end
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_release_spec.rb b/spec/features/issues/filtered_search/dropdown_release_spec.rb
index 2210a26c251..50ac9068b26 100644
--- a/spec/features/issues/filtered_search/dropdown_release_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_release_spec.rb
@@ -5,16 +5,15 @@ require 'spec_helper'
RSpec.describe 'Dropdown release', :js do
include FilteredSearchHelpers
- let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:release) { create(:release, tag: 'v1.0', project: project) }
let_it_be(:crazy_release) { create(:release, tag: '☺!/"#%&\'{}+,-.<>;=@]_`{|}🚀', project: project) }
let_it_be(:issue) { create(:issue, project: project) }
- let(:filtered_search) { find('.filtered-search') }
- let(:filter_dropdown) { find('#js-dropdown-release .filter-dropdown') }
-
before do
+ stub_feature_flags(vue_issues_list: true)
+
project.add_maintainer(user)
sign_in(user)
@@ -22,12 +21,11 @@ RSpec.describe 'Dropdown release', :js do
end
describe 'behavior' do
- before do
- filtered_search.set('release:=')
- end
-
it 'loads all the releases when opened' do
- expect_filtered_search_dropdown_results(filter_dropdown, 2)
+ select_tokens 'Release', '='
+
+ # Expect None, Any, v1.0, !/\"#%&'{}+,-.<>;=@]_`{|}
+ expect_suggestion_count 4
end
end
end
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index 1375384d1aa..13bce49e6d1 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -6,13 +6,8 @@ RSpec.describe 'Filter issues', :js do
include FilteredSearchHelpers
let(:project) { create(:project) }
-
- # NOTE: The short name here is actually important
- #
- # When the name is longer, the filtered search input can end up scrolling
- # horizontally, and PhantomJS can't handle it.
- let(:user) { create(:user, name: 'Ann') }
- let(:user2) { create(:user, name: 'jane') }
+ let(:user) { create(:user) }
+ let(:user2) { create(:user) }
let!(:bug_label) { create(:label, project: project, title: 'bug') }
let!(:caps_sensitive_label) { create(:label, project: project, title: 'CaPs') }
@@ -24,6 +19,7 @@ RSpec.describe 'Filter issues', :js do
end
before do
+ stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user)
create(:issue, project: project, author: user2, title: "Bug report 1")
@@ -64,31 +60,25 @@ RSpec.describe 'Filter issues', :js do
it 'filters by all available tokens' do
search_term = 'issue'
+ select_tokens 'Assignee', '=', user.username, 'Author', '=', user.username, 'Label', '=', caps_sensitive_label.title, 'Milestone', '=', milestone.title
+ send_keys search_term, :enter
- input_filtered_search("assignee:=@#{user.username} author:=@#{user.username} label:=~#{caps_sensitive_label.title} milestone:=%#{milestone.title} #{search_term}")
-
- wait_for_requests
-
- expect_tokens([
- assignee_token(user.name),
- author_token(user.name),
- label_token(caps_sensitive_label.title),
- milestone_token(milestone.title)
- ])
+ expect_assignee_token(user.name)
+ expect_author_token(user.name)
+ expect_label_token(caps_sensitive_label.title)
+ expect_milestone_token(milestone.title)
expect_issues_list_count(1)
- expect_filtered_search_input(search_term)
+ expect_search_term(search_term)
end
describe 'filter issues by author' do
context 'only author' do
it 'filters issues by searched author' do
- input_filtered_search("author:=@#{user.username}")
+ select_tokens 'Author', '=', user.username, submit: true
- wait_for_requests
-
- expect_tokens([author_token(user.name)])
+ expect_author_token(user.name)
expect_issues_list_count(5)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
end
end
@@ -96,46 +86,30 @@ RSpec.describe 'Filter issues', :js do
describe 'filter issues by assignee' do
context 'only assignee' do
it 'filters issues by searched assignee' do
- input_filtered_search("assignee:=@#{user.username}")
-
- wait_for_requests
+ select_tokens 'Assignee', '=', user.username, submit: true
- expect_tokens([assignee_token(user.name)])
+ expect_assignee_token(user.name)
expect_issues_list_count(5)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by no assignee' do
- input_filtered_search('assignee:=none')
+ select_tokens 'Assignee', '=', 'None', submit: true
- expect_tokens([assignee_token('None')])
+ expect_assignee_token 'None'
expect_issues_list_count(3)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by invalid assignee' do
skip('to be tested, issue #26546')
end
-
- it 'filters issues by multiple assignees' do
- create(:issue, project: project, author: user, assignees: [user2, user])
-
- input_filtered_search("assignee:=@#{user.username} assignee:=@#{user2.username}")
-
- expect_tokens([
- assignee_token(user.name),
- assignee_token(user2.name)
- ])
-
- expect_issues_list_count(1)
- expect_filtered_search_input_empty
- end
end
end
describe 'filter by reviewer' do
it 'does not allow filtering by reviewer' do
- find('.filtered-search').click
+ click_filtered_search_bar
expect(page).not_to have_button('Reviewer')
end
@@ -144,57 +118,53 @@ RSpec.describe 'Filter issues', :js do
describe 'filter issues by label' do
context 'only label' do
it 'filters issues by searched label' do
- input_filtered_search("label:=~#{bug_label.title}")
+ select_tokens 'Label', '=', bug_label.title, submit: true
- expect_tokens([label_token(bug_label.title)])
+ expect_label_token(bug_label.title)
expect_issues_list_count(2)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues not containing searched label' do
- input_filtered_search("label:!=~#{bug_label.title}")
+ select_tokens 'Label', '!=', bug_label.title, submit: true
- expect_tokens([label_token(bug_label.title)])
+ expect_negated_label_token(bug_label.title)
expect_issues_list_count(6)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by any label' do
- input_filtered_search('label:=any')
+ select_tokens 'Label', '=', 'Any', submit: true
- expect_tokens([label_token('Any', false)])
+ expect_label_token 'Any'
expect_issues_list_count(4)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by no label' do
- input_filtered_search('label:=none')
+ select_tokens 'Label', '=', 'None', submit: true
- expect_tokens([label_token('None', false)])
+ expect_label_token 'None'
expect_issues_list_count(4)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by multiple labels' do
- input_filtered_search("label:=~#{bug_label.title} label:=~#{caps_sensitive_label.title}")
+ select_tokens 'Label', '=', bug_label.title, 'Label', '=', caps_sensitive_label.title, submit: true
- expect_tokens([
- label_token(bug_label.title),
- label_token(caps_sensitive_label.title)
- ])
+ expect_label_token(bug_label.title)
+ expect_label_token(caps_sensitive_label.title)
expect_issues_list_count(1)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by multiple labels with not operator' do
- input_filtered_search("label:!=~#{bug_label.title} label:=~#{caps_sensitive_label.title}")
+ select_tokens 'Label', '!=', bug_label.title, 'Label', '=', caps_sensitive_label.title, submit: true
- expect_tokens([
- label_token(bug_label.title),
- label_token(caps_sensitive_label.title)
- ])
+ expect_negated_label_token(bug_label.title)
+ expect_label_token(caps_sensitive_label.title)
expect_issues_list_count(1)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by label containing special characters' do
@@ -202,11 +172,11 @@ RSpec.describe 'Filter issues', :js do
special_issue = create(:issue, title: "Issue with special character label", project: project)
special_issue.labels << special_label
- input_filtered_search("label:=~#{special_label.title}")
+ select_tokens 'Label', '=', special_label.title, submit: true
- expect_tokens([label_token(special_label.title)])
+ expect_label_token(special_label.title)
expect_issues_list_count(1)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by label not containing special characters' do
@@ -214,21 +184,21 @@ RSpec.describe 'Filter issues', :js do
special_issue = create(:issue, title: "Issue with special character label", project: project)
special_issue.labels << special_label
- input_filtered_search("label:!=~#{special_label.title}")
+ select_tokens 'Label', '!=', special_label.title, submit: true
- expect_tokens([label_token(special_label.title)])
+ expect_negated_label_token(special_label.title)
expect_issues_list_count(8)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'does not show issues for unused labels' do
new_label = create(:label, project: project, title: 'new_label')
- input_filtered_search("label:=~#{new_label.title}")
+ select_tokens 'Label', '=', new_label.title, submit: true
- expect_tokens([label_token(new_label.title)])
+ expect_label_token(new_label.title)
expect_no_issues_list
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
end
@@ -238,31 +208,28 @@ RSpec.describe 'Filter issues', :js do
special_multiple_issue = create(:issue, title: "Issue with special character multiple words label", project: project)
special_multiple_issue.labels << special_multiple_label
- input_filtered_search("label:=~'#{special_multiple_label.title}'")
+ select_tokens 'Label', '=', special_multiple_label.title, submit: true
# Check for search results (which makes sure that the page has changed)
expect_issues_list_count(1)
-
- # filtered search defaults quotations to double quotes
- expect_tokens([label_token("\"#{special_multiple_label.title}\"")])
-
- expect_filtered_search_input_empty
+ expect_label_token(special_multiple_label.title)
+ expect_empty_search_term
end
it 'single quotes' do
- input_filtered_search("label:=~'#{multiple_words_label.title}'")
+ select_tokens 'Label', '=', multiple_words_label.title, submit: true
expect_issues_list_count(1)
- expect_tokens([label_token("\"#{multiple_words_label.title}\"")])
- expect_filtered_search_input_empty
+ expect_label_token(multiple_words_label.title)
+ expect_empty_search_term
end
it 'double quotes' do
- input_filtered_search("label:=~\"#{multiple_words_label.title}\"")
+ select_tokens 'Label', '=', multiple_words_label.title, submit: true
- expect_tokens([label_token("\"#{multiple_words_label.title}\"")])
+ expect_label_token(multiple_words_label.title)
expect_issues_list_count(1)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'single quotes containing double quotes' do
@@ -270,11 +237,11 @@ RSpec.describe 'Filter issues', :js do
double_quotes_label_issue = create(:issue, title: "Issue with double quotes label", project: project)
double_quotes_label_issue.labels << double_quotes_label
- input_filtered_search("label:=~'#{double_quotes_label.title}'")
+ select_tokens 'Label', '=', double_quotes_label.title, submit: true
- expect_tokens([label_token("'#{double_quotes_label.title}'")])
+ expect_label_token(double_quotes_label.title)
expect_issues_list_count(1)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'double quotes containing single quotes' do
@@ -282,49 +249,41 @@ RSpec.describe 'Filter issues', :js do
single_quotes_label_issue = create(:issue, title: "Issue with single quotes label", project: project)
single_quotes_label_issue.labels << single_quotes_label
- input_filtered_search("label:=~\"#{single_quotes_label.title}\"")
+ select_tokens 'Label', '=', single_quotes_label.title, submit: true
- expect_tokens([label_token("\"#{single_quotes_label.title}\"")])
+ expect_label_token(single_quotes_label.title)
expect_issues_list_count(1)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
end
context 'multiple labels with other filters' do
it 'filters issues by searched label, label2, author, assignee, milestone and text' do
search_term = 'bug'
-
- input_filtered_search("label:=~#{bug_label.title} label:=~#{caps_sensitive_label.title} author:=@#{user.username} assignee:=@#{user.username} milestone:=%#{milestone.title} #{search_term}")
-
- wait_for_requests
-
- expect_tokens([
- label_token(bug_label.title),
- label_token(caps_sensitive_label.title),
- author_token(user.name),
- assignee_token(user.name),
- milestone_token(milestone.title)
- ])
+ select_tokens 'Label', '=', bug_label.title, 'Label', '=', caps_sensitive_label.title, 'Author', '=', user.username, 'Assignee', '=', user.username, 'Milestone', '=', milestone.title
+ send_keys search_term, :enter
+
+ expect_label_token(bug_label.title)
+ expect_label_token(caps_sensitive_label.title)
+ expect_author_token(user.name)
+ expect_assignee_token(user.name)
+ expect_milestone_token(milestone.title)
expect_issues_list_count(1)
- expect_filtered_search_input(search_term)
+ expect_search_term(search_term)
end
it 'filters issues by searched label, label2, author, assignee, not included in a milestone' do
search_term = 'bug'
-
- input_filtered_search("label:=~#{bug_label.title} label:=~#{caps_sensitive_label.title} author:=@#{user.username} assignee:=@#{user.username} milestone:!=%#{milestone.title} #{search_term}")
-
- wait_for_requests
-
- expect_tokens([
- label_token(bug_label.title),
- label_token(caps_sensitive_label.title),
- author_token(user.name),
- assignee_token(user.name),
- milestone_token(milestone.title, false, '!=')
- ])
+ select_tokens 'Label', '=', bug_label.title, 'Label', '=', caps_sensitive_label.title, 'Author', '=', user.username, 'Assignee', '=', user.username, 'Milestone', '!=', milestone.title
+ send_keys search_term, :enter
+
+ expect_label_token(bug_label.title)
+ expect_label_token(caps_sensitive_label.title)
+ expect_author_token(user.name)
+ expect_assignee_token(user.name)
+ expect_negated_milestone_token(milestone.title)
expect_issues_list_count(0)
- expect_filtered_search_input(search_term)
+ expect_search_term(search_term)
end
end
@@ -333,8 +292,8 @@ RSpec.describe 'Filter issues', :js do
click_link multiple_words_label.title
expect_issues_list_count(1)
- expect_tokens([label_token("\"#{multiple_words_label.title}\"")])
- expect_filtered_search_input_empty
+ expect_label_token(multiple_words_label.title)
+ expect_empty_search_term
end
end
end
@@ -342,19 +301,19 @@ RSpec.describe 'Filter issues', :js do
describe 'filter issues by milestone' do
context 'only milestone' do
it 'filters issues by searched milestone' do
- input_filtered_search("milestone:=%#{milestone.title}")
+ select_tokens 'Milestone', '=', milestone.title, submit: true
- expect_tokens([milestone_token(milestone.title)])
+ expect_milestone_token(milestone.title)
expect_issues_list_count(5)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by no milestone' do
- input_filtered_search("milestone:=none")
+ select_tokens 'Milestone', '=', 'None', submit: true
- expect_tokens([milestone_token('None', false)])
+ expect_milestone_token 'None'
expect_issues_list_count(3)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by upcoming milestones' do
@@ -362,11 +321,11 @@ RSpec.describe 'Filter issues', :js do
create(:issue, project: project, milestone: future_milestone, author: user)
end
- input_filtered_search("milestone:=upcoming")
+ select_tokens 'Milestone', '=', 'Upcoming', submit: true
- expect_tokens([milestone_token('Upcoming', false)])
+ expect_milestone_token 'Upcoming'
expect_issues_list_count(1)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by negation of upcoming milestones' do
@@ -378,72 +337,72 @@ RSpec.describe 'Filter issues', :js do
create(:issue, project: project, milestone: past_milestone, author: user)
end
- input_filtered_search("milestone:!=upcoming")
+ select_tokens 'Milestone', '!=', 'Upcoming', submit: true
- expect_tokens([milestone_token('Upcoming', false, '!=')])
+ expect_negated_milestone_token 'Upcoming'
expect_issues_list_count(1)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by started milestones' do
- input_filtered_search("milestone:=started")
+ select_tokens 'Milestone', '=', 'Started', submit: true
- expect_tokens([milestone_token('Started', false)])
+ expect_milestone_token 'Started'
expect_issues_list_count(5)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by negation of started milestones' do
milestone2 = create(:milestone, title: "9", project: project, start_date: 2.weeks.from_now)
create(:issue, project: project, author: user, title: "something else", milestone: milestone2)
- input_filtered_search("milestone:!=started")
+ select_tokens 'Milestone', '!=', 'Started', submit: true
- expect_tokens([milestone_token('Started', false, '!=')])
+ expect_negated_milestone_token 'Started'
expect_issues_list_count(1)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by milestone containing special characters' do
special_milestone = create(:milestone, title: '!@\#{$%^&*()}', project: project)
create(:issue, project: project, milestone: special_milestone)
- input_filtered_search("milestone:=%#{special_milestone.title}")
+ select_tokens 'Milestone', '=', special_milestone.title, submit: true
- expect_tokens([milestone_token(special_milestone.title)])
+ expect_milestone_token(special_milestone.title)
expect_issues_list_count(1)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'filters issues by milestone not containing special characters' do
special_milestone = create(:milestone, title: '!@\#{$%^&*()}', project: project)
create(:issue, project: project, milestone: special_milestone)
- input_filtered_search("milestone:!=%#{special_milestone.title}")
+ select_tokens 'Milestone', '!=', special_milestone.title, submit: true
- expect_tokens([milestone_token(special_milestone.title, false, '!=')])
+ expect_negated_milestone_token(special_milestone.title)
expect_issues_list_count(8)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'does not show issues for unused milestones' do
new_milestone = create(:milestone, title: 'new', project: project)
- input_filtered_search("milestone:=%#{new_milestone.title}")
+ select_tokens 'Milestone', '=', new_milestone.title, submit: true
- expect_tokens([milestone_token(new_milestone.title)])
+ expect_milestone_token(new_milestone.title)
expect_no_issues_list
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
it 'show issues for unused milestones' do
new_milestone = create(:milestone, title: 'new', project: project)
- input_filtered_search("milestone:!=%#{new_milestone.title}")
+ select_tokens 'Milestone', '!=', new_milestone.title, submit: true
- expect_tokens([milestone_token(new_milestone.title, false, '!=')])
+ expect_negated_milestone_token(new_milestone.title)
expect_issues_list_count(8)
- expect_filtered_search_input_empty
+ expect_empty_search_term
end
end
end
@@ -452,47 +411,47 @@ RSpec.describe 'Filter issues', :js do
context 'only text' do
it 'filters issues by searched text' do
search = 'Bug'
- input_filtered_search(search)
+ submit_search_term(search)
expect_issues_list_count(4)
- expect_filtered_search_input(search)
+ expect_search_term(search)
end
it 'filters issues by multiple searched text' do
search = 'Bug report'
- input_filtered_search(search)
+ submit_search_term(search)
expect_issues_list_count(3)
- expect_filtered_search_input(search)
+ expect_search_term(search)
end
it 'filters issues by case insensitive searched text' do
search = 'bug report'
- input_filtered_search(search)
+ submit_search_term(search)
expect_issues_list_count(3)
- expect_filtered_search_input(search)
+ expect_search_term(search)
end
it 'filters issues by searched text containing single quotes' do
issue = create(:issue, project: project, author: user, title: "issue with 'single quotes'")
- search = "'single quotes'"
- input_filtered_search(search)
+ search = 'single quotes'
+ submit_search_term "'#{search}'"
expect_issues_list_count(1)
- expect_filtered_search_input(search)
+ expect_search_term(search)
expect(page).to have_content(issue.title)
end
it 'filters issues by searched text containing double quotes' do
issue = create(:issue, project: project, author: user, title: "issue with \"double quotes\"")
- search = '"double quotes"'
- input_filtered_search(search)
+ search = 'double quotes'
+ submit_search_term "\"#{search}\""
expect_issues_list_count(1)
- expect_filtered_search_input(search)
+ expect_search_term(search)
expect(page).to have_content(issue.title)
end
@@ -502,36 +461,43 @@ RSpec.describe 'Filter issues', :js do
issue = create(:issue, project: project, author: user, title: "issue with !@\#{$%^&*()-+")
search = '!@#{$%^&*()-+'
- input_filtered_search(search)
+ submit_search_term(search)
expect_issues_list_count(1)
- expect_filtered_search_input(search)
+ expect_search_term(search)
expect(page).to have_content(issue.title)
end
it 'does not show any issues' do
search = 'testing'
- input_filtered_search(search)
+ submit_search_term(search)
expect_no_issues_list
- expect_filtered_search_input(search)
+ expect_search_term(search)
end
it 'filters issues by issue reference' do
search = '#1'
- input_filtered_search(search)
+ submit_search_term(search)
expect_issues_list_count(1)
- expect_filtered_search_input(search)
+ expect_search_term(search)
end
end
context 'searched text with other filters' do
it 'filters issues by searched text, author, text, assignee, text, label1, text, label2, text, milestone and text' do
- input_filtered_search("bug author:=@#{user.username} report label:=~#{bug_label.title} label:=~#{caps_sensitive_label.title} milestone:=%#{milestone.title} foo")
+ click_filtered_search_bar
+ send_keys 'bug '
+ select_tokens 'Author', '=', user.username
+ send_keys 'report '
+ select_tokens 'Label', '=', bug_label.title
+ select_tokens 'Label', '=', caps_sensitive_label.title
+ select_tokens 'Milestone', '=', milestone.title
+ send_keys 'foo', :enter
expect_issues_list_count(1)
- expect_filtered_search_input('bug report foo')
+ expect_search_term('bug report foo')
end
end
@@ -549,17 +515,11 @@ RSpec.describe 'Filter issues', :js do
author: user,
created_at: 5.days.ago)
- input_filtered_search('days ago')
+ submit_search_term 'days ago'
expect_issues_list_count(2)
-
- sort_toggle = find('.filter-dropdown-container .dropdown')
- sort_toggle.click
-
- find('.filter-dropdown-container .dropdown-menu li a', text: 'Created date').click
- wait_for_requests
-
- expect(find('.issues-list .issue:first-of-type .issue-title-text a')).to have_content(new_issue.title)
+ expect(page).to have_button 'Created date'
+ expect(page).to have_css('.issue:first-of-type .issue-title', text: new_issue.title)
end
end
end
@@ -568,7 +528,7 @@ RSpec.describe 'Filter issues', :js do
let!(:closed_issue) { create(:issue, :closed, project: project, title: 'closed bug') }
before do
- input_filtered_search('bug')
+ submit_search_term 'bug'
# This ensures that the search is performed
expect_issues_list_count(4, 1)
@@ -599,19 +559,17 @@ RSpec.describe 'Filter issues', :js do
end
it 'milestone dropdown loads milestones' do
- input_filtered_search("milestone:=", submit: false)
+ select_tokens 'Milestone', '='
- within('#js-dropdown-milestone') do
- expect(page).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1)
- end
+ # Expect None, Any, Upcoming, Started, 8
+ expect_suggestion_count 5
end
it 'label dropdown load labels' do
- input_filtered_search("label:=", submit: false)
+ select_tokens 'Label', '='
- within('#js-dropdown-label') do
- expect(page).to have_selector('.filter-dropdown .filter-dropdown-item', count: 3)
- end
+ # Dropdown shows None, Any, and 3 labels
+ expect_suggestion_count 5
end
end
end
diff --git a/spec/features/issues/filtered_search/recent_searches_spec.rb b/spec/features/issues/filtered_search/recent_searches_spec.rb
index 3929d3694ff..bb5964258be 100644
--- a/spec/features/issues/filtered_search/recent_searches_spec.rb
+++ b/spec/features/issues/filtered_search/recent_searches_spec.rb
@@ -4,7 +4,6 @@ require 'spec_helper'
RSpec.describe 'Recent searches', :js do
include FilteredSearchHelpers
- include MobileHelpers
let_it_be(:project_1) { create(:project, :public) }
let_it_be(:project_2) { create(:project, :public) }
@@ -14,116 +13,96 @@ RSpec.describe 'Recent searches', :js do
let(:project_1_local_storage_key) { "#{project_1.full_path}-issue-recent-searches" }
before do
- Capybara.ignore_hidden_elements = false
+ stub_feature_flags(vue_issues_list: true)
# Visit any fast-loading page so we can clear local storage without a DOM exception
visit '/404'
remove_recent_searches
end
- after do
- Capybara.ignore_hidden_elements = true
- end
-
it 'searching adds to recent searches' do
visit project_issues_path(project_1)
- input_filtered_search('foo', submit: true)
- input_filtered_search('bar', submit: true)
-
- items = all('.filtered-search-history-dropdown-item', visible: false, count: 2)
+ submit_then_clear_search 'foo'
+ submit_then_clear_search 'bar'
+ click_button 'Toggle history'
- expect(items[0].text).to eq('bar')
- expect(items[1].text).to eq('foo')
+ expect_recent_searches_history_item 'bar'
+ expect_recent_searches_history_item 'foo'
end
it 'visiting URL with search params adds to recent searches' do
visit project_issues_path(project_1, label_name: 'foo', search: 'bar')
visit project_issues_path(project_1, label_name: 'qux', search: 'garply')
- items = all('.filtered-search-history-dropdown-item', visible: false, count: 2)
+ click_button 'Toggle history'
- expect(items[0].text).to eq('label: = ~qux garply')
- expect(items[1].text).to eq('label: = ~foo bar')
+ expect_recent_searches_history_item 'Label := qux garply'
+ expect_recent_searches_history_item 'Label := foo bar'
end
it 'saved recent searches are restored last on the list' do
- set_recent_searches(project_1_local_storage_key, '["saved1", "saved2"]')
+ set_recent_searches(project_1_local_storage_key, '[[{"type":"filtered-search-term","value":{"data":"saved1"}}],[{"type":"filtered-search-term","value":{"data":"saved2"}}]]')
visit project_issues_path(project_1, search: 'foo')
+ click_button 'Toggle history'
- items = all('.filtered-search-history-dropdown-item', visible: false, count: 3)
-
- expect(items[0].text).to eq('foo')
- expect(items[1].text).to eq('saved1')
- expect(items[2].text).to eq('saved2')
+ expect_recent_searches_history_item 'foo'
+ expect_recent_searches_history_item 'saved1'
+ expect_recent_searches_history_item 'saved2'
end
it 'searches are scoped to projects' do
visit project_issues_path(project_1)
- input_filtered_search('foo', submit: true)
- input_filtered_search('bar', submit: true)
+ submit_then_clear_search 'foo'
+ submit_then_clear_search 'bar'
visit project_issues_path(project_2)
- input_filtered_search('more', submit: true)
- input_filtered_search('things', submit: true)
-
- items = all('.filtered-search-history-dropdown-item', visible: false, count: 2)
+ submit_then_clear_search 'more'
+ submit_then_clear_search 'things'
+ click_button 'Toggle history'
- expect(items[0].text).to eq('things')
- expect(items[1].text).to eq('more')
+ expect_recent_searches_history_item 'things'
+ expect_recent_searches_history_item 'more'
end
it 'clicking item fills search input' do
- set_recent_searches(project_1_local_storage_key, '["foo", "bar"]')
+ set_recent_searches(project_1_local_storage_key, '[[{"type":"filtered-search-term","value":{"data":"foo"}}],[{"type":"filtered-search-term","value":{"data":"bar"}}]]')
visit project_issues_path(project_1)
- find('.filtered-search-history-dropdown-toggle-button').click
- all('.filtered-search-history-dropdown-item', count: 2)[0].click
- wait_for_filtered_search('foo')
+ click_button 'Toggle history'
+ click_button 'foo'
- expect(find('.filtered-search').value.strip).to eq('foo')
+ expect_search_term 'foo'
end
it 'clear recent searches button, clears recent searches' do
- set_recent_searches(project_1_local_storage_key, '["foo"]')
+ set_recent_searches(project_1_local_storage_key, '[[{"type":"filtered-search-term","value":{"data":"foo"}}]]')
visit project_issues_path(project_1)
- find('.filtered-search-history-dropdown-toggle-button').click
- all('.filtered-search-history-dropdown-item', count: 1)
+ click_button 'Toggle history'
- find('.filtered-search-history-clear-button').click
- items_after = all('.filtered-search-history-dropdown-item', count: 0)
+ expect_recent_searches_history_item_count 1
- expect(items_after.count).to eq(0)
+ click_button 'Clear recent searches'
+ click_button 'Toggle history'
+
+ expect(page).to have_text "You don't have any recent searches"
+ expect_recent_searches_history_item_count 0
end
it 'shows flash error when failed to parse saved history' do
set_recent_searches(project_1_local_storage_key, 'fail')
visit project_issues_path(project_1)
- expect(find('[data-testid="alert-danger"]')).to have_text('An error occurred while parsing recent searches')
+ expect(page).to have_text 'An error occurred while parsing recent searches'
end
- context 'on tablet/mobile screen' do
- it 'shows only the history icon in the dropdown' do
- resize_screen_sm
- visit project_issues_path(project_1)
-
- expect(find('.filtered-search-history-dropdown-wrapper')).to have_selector('svg', visible: true)
- expect(find('.filtered-search-history-dropdown-wrapper')).to have_selector('span', text: 'Recent searches', visible: false)
- end
- end
-
- context 'on PC screen' do
- it 'shows only the Recent searches text in the dropdown' do
- restore_window_size
- visit project_issues_path(project_1)
-
- expect(find('.filtered-search-history-dropdown-wrapper')).to have_selector('svg', visible: false)
- expect(find('.filtered-search-history-dropdown-wrapper')).to have_selector('span', text: 'Recent searches', visible: true)
- end
+ def submit_then_clear_search(search)
+ click_filtered_search_bar
+ send_keys(search, :enter)
+ click_button 'Clear'
end
end
diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb
index 60963d95ae5..8639ec2a227 100644
--- a/spec/features/issues/filtered_search/search_bar_spec.rb
+++ b/spec/features/issues/filtered_search/search_bar_spec.rb
@@ -9,102 +9,74 @@ RSpec.describe 'Search bar', :js do
let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) }
- let(:filtered_search) { find('.filtered-search') }
-
before do
+ stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user)
sign_in(user)
visit project_issues_path(project)
end
- def get_left_style(style)
- left_style = /left:\s\d*[.]\d*px/.match(style)
- left_style.to_s.gsub('left: ', '').to_f
- end
-
describe 'keyboard navigation' do
- it 'makes item active' do
- filtered_search.native.send_keys(:down)
-
- page.within '#js-dropdown-hint' do
- expect(page).to have_selector('.droplab-item-active')
- end
- end
-
it 'selects item' do
- filtered_search.native.send_keys(:down, :down, :enter)
+ click_filtered_search_bar
+ send_keys :down, :enter
- expect_tokens([{ name: 'Assignee' }])
- expect_filtered_search_input_empty
+ expect_token_segment 'Assignee'
end
end
describe 'clear search button' do
it 'clears text' do
search_text = 'search_text'
- filtered_search.set(search_text)
+ click_filtered_search_bar
+ send_keys search_text
+
+ expect(page).to have_field 'Search', with: search_text
- expect(filtered_search.value).to eq(search_text)
- find('.filtered-search-box .clear-search').click
+ click_button 'Clear'
- expect(filtered_search.value).to eq('')
+ expect(page).to have_field 'Search', with: ''
end
it 'hides by default' do
- expect(page).to have_css('.clear-search', visible: false)
+ expect(page).not_to have_button 'Clear'
end
it 'hides after clicked' do
- filtered_search.set('a')
- find('.filtered-search-box .clear-search').click
+ click_filtered_search_bar
+ send_keys 'a'
- expect(page).to have_css('.clear-search', visible: false)
+ click_button 'Clear'
+
+ expect(page).not_to have_button 'Clear'
end
it 'hides when there is no text' do
- filtered_search.set('a')
- filtered_search.set('')
+ click_filtered_search_bar
+ send_keys 'a', :backspace, :backspace
- expect(page).to have_css('.clear-search', visible: false)
+ expect(page).not_to have_button 'Clear'
end
it 'shows when there is text' do
- filtered_search.set('a')
+ click_filtered_search_bar
+ send_keys 'a'
- expect(page).to have_css('.clear-search', visible: true)
+ expect(page).to have_button 'Clear'
end
it 'resets the dropdown hint filter' do
- filtered_search.click
- original_size = page.all('#js-dropdown-hint .filter-dropdown .filter-dropdown-item').size
-
- filtered_search.set('autho')
-
- expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1)
-
- find('.filtered-search-box .clear-search').click
- filtered_search.click
-
- expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: original_size)
- end
-
- it 'resets the dropdown filters' do
- filtered_search.click
-
- hint_offset = get_left_style(find('#js-dropdown-hint')['style'])
-
- filtered_search.set('a')
-
- filtered_search.set('author:')
+ click_filtered_search_bar
+ original_size = get_suggestion_count
+ send_keys 'autho'
- find('#js-dropdown-hint', visible: false)
+ expect_suggestion_count 1
- find('.filtered-search-box .clear-search').click
- filtered_search.click
+ click_button 'Clear'
+ click_filtered_search_bar
- expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', minimum: 6)
- expect(get_left_style(find('#js-dropdown-hint')['style'])).to eq(hint_offset)
+ expect_suggestion_count(original_size)
end
end
end
diff --git a/spec/features/issues/filtered_search/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb
index 2d8587d886f..9fb6a4cc2af 100644
--- a/spec/features/issues/filtered_search/visual_tokens_spec.rb
+++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb
@@ -14,178 +14,160 @@ RSpec.describe 'Visual tokens', :js do
let_it_be(:cc_label) { create(:label, project: project, title: 'Community Contribution') }
let_it_be(:issue) { create(:issue, project: project) }
- let(:filtered_search) { find('.filtered-search') }
- let(:filter_author_dropdown) { find("#js-dropdown-author .filter-dropdown") }
-
- def is_input_focused
- page.evaluate_script("document.activeElement.classList.contains('filtered-search')")
- end
-
before do
+ stub_feature_flags(vue_issues_list: true)
project.add_user(user, :maintainer)
project.add_user(user_rock, :maintainer)
sign_in(user)
- set_cookie('sidebar_collapsed', 'true')
-
visit project_issues_path(project)
end
describe 'editing a single token' do
before do
- input_filtered_search('author:=@root assignee:=none', submit: false)
- first('.tokens-container .filtered-search-token').click
- wait_for_requests
+ select_tokens 'Author', '=', user.username, 'Assignee', '=', 'None'
+ click_token_segment(user.name)
end
it 'opens author dropdown' do
- expect(page).to have_css('#js-dropdown-author', visible: true)
- expect_filtered_search_input('@root')
+ expect_visible_suggestions_list
+ expect(page).to have_field('Search', with: 'root')
end
it 'filters value' do
- filtered_search.send_keys(:backspace)
+ send_keys :backspace
- expect(page).to have_css('#js-dropdown-author .filter-dropdown .filter-dropdown-item', count: 1)
+ expect_suggestion_count 1
end
it 'ends editing mode when document is clicked' do
find('.js-navbar').click
- expect_filtered_search_input_empty
- expect(page).to have_css('#js-dropdown-author', visible: false)
+ expect_empty_search_term
+ expect_hidden_suggestions_list
end
describe 'selecting different author from dropdown' do
before do
- filter_author_dropdown.find('.filter-dropdown-item .dropdown-light-content', text: "@#{user_rock.username}").click
+ send_keys :backspace, :backspace, :backspace, :backspace
+ click_on user_rock.name
end
it 'changes value in visual token' do
- wait_for_requests
- expect(first('.tokens-container .filtered-search-token .value').text).to eq("#{user_rock.name}")
- end
-
- it 'moves input to the right' do
- expect(is_input_focused).to eq(true)
+ expect_author_token(user_rock.name)
end
end
end
describe 'editing multiple tokens' do
before do
- input_filtered_search('author:=@root assignee:=none', submit: false)
- first('.tokens-container .filtered-search-token').click
+ select_tokens 'Author', '=', user.username, 'Assignee', '=', 'None'
+ click_token_segment(user.name)
end
it 'opens author dropdown' do
- expect(page).to have_css('#js-dropdown-author', visible: true)
+ expect_visible_suggestions_list
end
it 'opens assignee dropdown' do
- find('.tokens-container .filtered-search-token', text: 'Assignee').click
- expect(page).to have_css('#js-dropdown-assignee', visible: true)
+ click_token_segment 'Assignee'
+
+ expect_visible_suggestions_list
end
end
describe 'editing a search term while editing another filter token' do
before do
- input_filtered_search('foo assignee:=', submit: false)
- first('.tokens-container .filtered-search-term').click
+ click_filtered_search_bar
+ send_keys 'foo '
+ select_tokens 'Assignee', '='
+ click_token_segment 'foo'
+ send_keys ' '
end
it 'opens author dropdown' do
- find('#js-dropdown-hint .filter-dropdown .filter-dropdown-item', text: 'Author').click
+ click_on 'Author'
- expect(page).to have_css('#js-dropdown-operator', visible: true)
- expect(page).to have_css('#js-dropdown-author', visible: false)
+ expect_suggestion '='
+ expect_suggestion '!='
- find('#js-dropdown-operator .filter-dropdown .filter-dropdown-item[data-value="="]').click
+ click_on '= is'
- expect(page).to have_css('#js-dropdown-operator', visible: false)
- expect(page).to have_css('#js-dropdown-author', visible: true)
+ expect_suggestion(user.name)
+ expect_suggestion(user_rock.name)
end
end
describe 'add new token after editing existing token' do
before do
- input_filtered_search('author:=@root assignee:=none', submit: false)
- first('.tokens-container .filtered-search-token').click
- filtered_search.send_keys(' ')
+ select_tokens 'Assignee', '=', user.username, 'Label', '=', 'None'
+ click_token_segment(user.name)
+ send_keys ' '
end
describe 'opens dropdowns' do
it 'opens hint dropdown' do
- expect(page).to have_css('#js-dropdown-hint', visible: true)
+ expect_visible_suggestions_list
end
it 'opens token dropdown' do
- filtered_search.send_keys('author:=')
+ click_on 'Author'
- expect(page).to have_css('#js-dropdown-author', visible: true)
+ expect_visible_suggestions_list
end
end
describe 'visual tokens' do
it 'creates visual token' do
- filtered_search.send_keys('author:=@thomas ')
- token = page.all('.tokens-container .filtered-search-token')[1]
+ click_on 'Author'
+ click_on '= is'
+ click_on 'The Rock'
- expect(token.find('.name').text).to eq('Author')
- expect(token.find('.value').text).to eq('@thomas')
+ expect_author_token 'The Rock'
end
end
it 'does not tokenize incomplete token' do
- filtered_search.send_keys('author:=')
-
+ click_on 'Author'
find('.js-navbar').click
- token = page.all('.tokens-container .js-visual-token')[1]
- expect_filtered_search_input_empty
- expect(token.find('.name').text).to eq('Author')
+ expect_empty_search_term
+ expect_token_segment 'Assignee'
end
end
describe 'search using incomplete visual tokens' do
before do
- input_filtered_search('author:=@root assignee:=none', extra_space: false)
+ select_tokens 'Author', '=', user.username, 'Assignee', '=', 'None'
end
it 'tokenizes the search term to complete visual token' do
- expect_tokens([
- author_token(user.name),
- assignee_token('None')
- ])
+ expect_author_token(user.name)
+ expect_assignee_token 'None'
end
end
it 'does retain hint token when mix of typing and clicks are performed' do
- input_filtered_search('label:', extra_space: false, submit: false)
-
- expect(page).to have_css('#js-dropdown-operator', visible: true)
-
- find('#js-dropdown-operator li[data-value="="]').click
-
- token = page.all('.tokens-container .js-visual-token')[0]
+ select_tokens 'Label'
+ click_on '= is'
- expect(token.find('.name').text).to eq('Label')
- expect(token.find('.operator').text).to eq('=')
+ expect_token_segment 'Label'
+ expect_token_segment '='
end
describe 'Any/None option' do
it 'hidden when NOT operator is selected' do
- input_filtered_search('milestone:!=', extra_space: false, submit: false)
+ select_tokens 'Milestone', '!='
- expect(page).not_to have_selector("#js-dropdown-milestone", text: 'Any')
- expect(page).not_to have_selector("#js-dropdown-milestone", text: 'None')
+ expect_no_suggestion 'Any'
+ expect_no_suggestion 'None'
end
it 'shown when EQUAL operator is selected' do
- input_filtered_search('milestone:=', extra_space: false, submit: false)
+ select_tokens 'Milestone', '='
- expect(page).to have_selector("#js-dropdown-milestone", text: 'Any')
- expect(page).to have_selector("#js-dropdown-milestone", text: 'None')
+ expect_suggestion 'Any'
+ expect_suggestion 'None'
end
end
end
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 6f4a13c5fad..8732e2ecff2 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -445,7 +445,7 @@ RSpec.describe 'GFM autocomplete', :js do
click_button('Cancel')
page.within('.modal') do
- click_button('OK', match: :first)
+ click_button('Discard changes', match: :first)
end
wait_for_requests
diff --git a/spec/features/issues/rss_spec.rb b/spec/features/issues/rss_spec.rb
index b20502ecc25..bdc5f282875 100644
--- a/spec/features/issues/rss_spec.rb
+++ b/spec/features/issues/rss_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project Issues RSS' do
+RSpec.describe 'Project Issues RSS', :js do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
@@ -13,6 +13,10 @@ RSpec.describe 'Project Issues RSS' do
group.add_developer(user)
end
+ before do
+ stub_feature_flags(vue_issues_list: true)
+ end
+
context 'when signed in' do
let_it_be(:user) { create(:user) }
@@ -25,7 +29,10 @@ RSpec.describe 'Project Issues RSS' do
visit path
end
- it_behaves_like "it has an RSS button with current_user's feed token"
+ it "shows the RSS button with current_user's feed token" do
+ expect(page).to have_link 'Subscribe to RSS feed', href: /feed_token=#{user.feed_token}/
+ end
+
it_behaves_like "an autodiscoverable RSS feed with current_user's feed token"
end
@@ -34,7 +41,10 @@ RSpec.describe 'Project Issues RSS' do
visit path
end
- it_behaves_like "it has an RSS button without a feed token"
+ it "shows the RSS button without a feed token" do
+ expect(page).not_to have_link 'Subscribe to RSS feed', href: /feed_token/
+ end
+
it_behaves_like "an autodiscoverable RSS feed without a feed token"
end
diff --git a/spec/features/issues/user_bulk_edits_issues_labels_spec.rb b/spec/features/issues/user_bulk_edits_issues_labels_spec.rb
index 71213fb661f..27377f6e1fd 100644
--- a/spec/features/issues/user_bulk_edits_issues_labels_spec.rb
+++ b/spec/features/issues/user_bulk_edits_issues_labels_spec.rb
@@ -12,8 +12,12 @@ RSpec.describe 'Issues > Labels bulk assignment' do
let!(:issue1) { create(:issue, project: project, title: "Issue 1", labels: [frontend]) }
let!(:issue2) { create(:issue, project: project, title: "Issue 2") }
- let(:issue_1_selector) { "#issue_#{issue1.id}" }
- let(:issue_2_selector) { "#issue_#{issue2.id}" }
+ let(:issue_1_selector) { "#issuable_#{issue1.id}" }
+ let(:issue_2_selector) { "#issuable_#{issue2.id}" }
+
+ before do
+ stub_feature_flags(vue_issues_list: true)
+ end
context 'as an allowed user', :js do
before do
diff --git a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
index ae1bce7ea4c..1c707466b51 100644
--- a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
+++ b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
@@ -25,6 +25,20 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do
sign_in(user)
end
+ context 'when ’Create merge request’ button is clicked' do
+ before do
+ visit project_issue_path(project, issue)
+
+ wait_for_requests
+
+ click_button('Create merge request')
+
+ wait_for_requests
+ end
+
+ it_behaves_like 'merge request author auto assign'
+ end
+
context 'when interacting with the dropdown' do
before do
visit project_issue_path(project, issue)
diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb
index 8a5e33ba18c..3bba041dab7 100644
--- a/spec/features/issues/user_creates_issue_spec.rb
+++ b/spec/features/issues/user_creates_issue_spec.rb
@@ -8,12 +8,16 @@ RSpec.describe "User creates issue" do
let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:user) { create(:user) }
+ before do
+ stub_feature_flags(vue_issues_list: true)
+ end
+
context "when unauthenticated" do
before do
sign_out(:user)
end
- it "redirects to signin then back to new issue after signin" do
+ it "redirects to signin then back to new issue after signin", :js do
create(:issue, project: project)
visit project_issues_path(project)
diff --git a/spec/features/issues/user_filters_issues_spec.rb b/spec/features/issues/user_filters_issues_spec.rb
index 5d05df6aaf0..42c2b5d32c1 100644
--- a/spec/features/issues/user_filters_issues_spec.rb
+++ b/spec/features/issues/user_filters_issues_spec.rb
@@ -7,6 +7,8 @@ RSpec.describe 'User filters issues', :js do
let_it_be(:project) { create(:project_empty_repo, :public) }
before do
+ stub_feature_flags(vue_issues_list: true)
+
%w[foobar barbaz].each do |title|
create(:issue,
author: user,
@@ -24,7 +26,7 @@ RSpec.describe 'User filters issues', :js do
let(:issue) { @issue }
it 'allows filtering by issues with no specified assignee' do
- visit project_issues_path(project, assignee_id: IssuableFinder::Params::FILTER_NONE)
+ visit project_issues_path(project, assignee_id: IssuableFinder::Params::FILTER_NONE.capitalize)
expect(page).to have_content 'foobar'
expect(page).not_to have_content 'barbaz'
diff --git a/spec/features/issues/user_scrolls_to_deeplinked_note_spec.rb b/spec/features/issues/user_scrolls_to_deeplinked_note_spec.rb
index 1fa8f533869..5aae5abaf10 100644
--- a/spec/features/issues/user_scrolls_to_deeplinked_note_spec.rb
+++ b/spec/features/issues/user_scrolls_to_deeplinked_note_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe 'User scrolls to deep-linked note' do
context 'on issue page', :js do
it 'on comment' do
+ stub_feature_flags(gl_avatar_for_all_user_avatars: false)
visit project_issue_path(project, issue, anchor: "note_#{comment_1.id}")
wait_for_requests
diff --git a/spec/features/issues/user_sees_breadcrumb_links_spec.rb b/spec/features/issues/user_sees_breadcrumb_links_spec.rb
index 669c7c45411..1577d7d5ce8 100644
--- a/spec/features/issues/user_sees_breadcrumb_links_spec.rb
+++ b/spec/features/issues/user_sees_breadcrumb_links_spec.rb
@@ -8,6 +8,8 @@ RSpec.describe 'New issue breadcrumb' do
let(:user) { project.creator }
before do
+ stub_feature_flags(vue_issues_list: true)
+
sign_in(user)
visit(new_project_issue_path(project))
end
@@ -24,10 +26,10 @@ RSpec.describe 'New issue breadcrumb' do
visit project_issue_path(project, issue)
- expect(find('.breadcrumbs-sub-title a')[:href]).to end_with(issue_path(issue))
+ expect(find('[data-testid="breadcrumb-current-link"] a')[:href]).to end_with(issue_path(issue))
end
- it 'excludes award_emoji from comment count' do
+ it 'excludes award_emoji from comment count', :js do
issue = create(:issue, author: user, assignees: [user], project: project, title: 'foobar')
create(:award_emoji, awardable: issue)
diff --git a/spec/features/issues/user_sorts_issues_spec.rb b/spec/features/issues/user_sorts_issues_spec.rb
index 86bdaf5d706..4af313576ed 100644
--- a/spec/features/issues/user_sorts_issues_spec.rb
+++ b/spec/features/issues/user_sorts_issues_spec.rb
@@ -16,6 +16,8 @@ RSpec.describe "User sorts issues" do
let_it_be(:later_due_milestone) { create(:milestone, project: project, due_date: '2013-12-12') }
before do
+ stub_feature_flags(vue_issues_list: true)
+
create_list(:award_emoji, 2, :upvote, awardable: issue1)
create_list(:award_emoji, 2, :downvote, awardable: issue2)
create(:award_emoji, :downvote, awardable: issue1)
@@ -24,26 +26,23 @@ RSpec.describe "User sorts issues" do
sign_in(user)
end
- it 'keeps the sort option' do
+ it 'keeps the sort option', :js do
visit(project_issues_path(project))
- find('.filter-dropdown-container .dropdown').click
-
- page.within('ul.dropdown-menu.dropdown-menu-right li') do
- click_link('Milestone')
- end
+ click_button 'Created date'
+ click_button 'Milestone'
visit(issues_dashboard_path(assignee_username: user.username))
- expect(find('.issues-filters a.is-active')).to have_content('Milestone')
+ expect(page).to have_button 'Milestone'
visit(project_issues_path(project))
- expect(find('.issues-filters a.is-active')).to have_content('Milestone')
+ expect(page).to have_button 'Milestone'
visit(issues_group_path(group))
- expect(find('.issues-filters a.is-active')).to have_content('Milestone')
+ expect(page).to have_button 'Milestone'
end
it 'sorts by popularity', :js do
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index 479199b72b7..ea888d4b254 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -17,6 +17,8 @@ RSpec.describe 'Labels Hierarchy', :js do
let!(:project_label_1) { create(:label, project: project_1, title: 'Label_4') }
before do
+ stub_feature_flags(vue_issues_list: true)
+
grandparent.add_owner(user)
sign_in(user)
@@ -34,8 +36,6 @@ RSpec.describe 'Labels Hierarchy', :js do
click_on 'Close'
end
- wait_for_requests
-
expect(page).to have_selector('.gl-label', text: label.title)
end
end
@@ -44,8 +44,6 @@ RSpec.describe 'Labels Hierarchy', :js do
page.within('.block.labels') do
click_on 'Edit'
- wait_for_requests
-
expect(page).not_to have_text(child_group_label.title)
end
end
@@ -54,15 +52,21 @@ RSpec.describe 'Labels Hierarchy', :js do
shared_examples 'filtering by ancestor labels for projects' do |board = false|
it 'filters by ancestor labels' do
[grandparent_group_label, parent_group_label, project_label_1].each do |label|
- select_label_on_dropdown(label.title)
-
- wait_for_requests
-
if board
+ select_label_on_dropdown(label.title)
+
expect(page).to have_selector('.board-card-title') do |card|
expect(card).to have_selector('a', text: labeled_issue.title)
end
else
+ within '[data-testid="filtered-search-input"]' do
+ click_filtered_search_bar
+ click_on 'Label'
+ click_on '= is'
+ click_on label.title
+ send_keys :enter
+ end
+
expect_issues_list_count(1)
expect(page).to have_selector('.issue-title', text: labeled_issue.title)
end
@@ -70,9 +74,11 @@ RSpec.describe 'Labels Hierarchy', :js do
end
it 'does not filter by descendant group labels' do
- filtered_search.set("label=")
-
- wait_for_requests
+ if board
+ filtered_search.set("label=")
+ else
+ select_tokens 'Label', '='
+ end
expect(page).not_to have_link child_group_label.title
end
@@ -93,11 +99,9 @@ RSpec.describe 'Labels Hierarchy', :js do
it 'filters by ancestors and current group labels' do
[grandparent_group_label, parent_group_label].each do |label|
- select_label_on_dropdown(label.title)
-
- wait_for_requests
-
if board
+ select_label_on_dropdown(label.title)
+
expect(page).to have_selector('.board-card-title') do |card|
expect(card).to have_selector('a', text: labeled_issue.title)
end
@@ -106,6 +110,14 @@ RSpec.describe 'Labels Hierarchy', :js do
expect(card).to have_selector('a', text: labeled_issue_2.title)
end
else
+ within '[data-testid="filtered-search-input"]' do
+ click_filtered_search_bar
+ click_on 'Label'
+ click_on '= is'
+ click_on label.title
+ send_keys :enter
+ end
+
expect_issues_list_count(3)
expect(page).to have_selector('.issue-title', text: labeled_issue.title)
expect(page).to have_selector('.issue-title', text: labeled_issue_2.title)
@@ -115,11 +127,9 @@ RSpec.describe 'Labels Hierarchy', :js do
end
it 'filters by descendant group labels' do
- wait_for_requests
-
- select_label_on_dropdown(group_label_3.title)
-
if board
+ select_label_on_dropdown(group_label_3.title)
+
expect(page).to have_selector('.board-card-title') do |card|
expect(card).not_to have_selector('a', text: labeled_issue_2.title)
end
@@ -128,17 +138,23 @@ RSpec.describe 'Labels Hierarchy', :js do
expect(card).to have_selector('a', text: labeled_issue_3.title)
end
else
+ select_tokens 'Label', '=', group_label_3.title, submit: true
+
expect_issues_list_count(1)
expect(page).to have_selector('.issue-title', text: labeled_issue_3.title)
end
end
it 'does not filter by descendant group project labels' do
- filtered_search.set("label=")
+ if board
+ filtered_search.set("label=")
- wait_for_requests
+ expect(page).not_to have_selector('.btn-link', text: project_label_3.title)
+ else
+ select_tokens 'Label', '='
- expect(page).not_to have_selector('.btn-link', text: project_label_3.title)
+ expect(page).not_to have_link project_label_3.title
+ end
end
end
@@ -195,9 +211,7 @@ RSpec.describe 'Labels Hierarchy', :js do
it_behaves_like 'filtering by ancestor labels for projects'
it 'does not filter by descendant group labels' do
- filtered_search.set("label=")
-
- wait_for_requests
+ select_tokens 'Label', '='
expect(page).not_to have_link child_group_label.title
end
diff --git a/spec/features/markdown/mermaid_spec.rb b/spec/features/markdown/mermaid_spec.rb
index 6a91d4e03c1..322b5306a00 100644
--- a/spec/features/markdown/mermaid_spec.rb
+++ b/spec/features/markdown/mermaid_spec.rb
@@ -5,6 +5,9 @@ require 'spec_helper'
RSpec.describe 'Mermaid rendering', :js do
let_it_be(:project) { create(:project, :public) }
+ let(:is_mac) { page.evaluate_script('navigator.platform').include?('Mac') }
+ let(:modifier_key) { is_mac ? :command : :control }
+
before do
stub_feature_flags(sandboxed_mermaid: false)
end
@@ -48,8 +51,8 @@ RSpec.describe 'Mermaid rendering', :js do
wait_for_requests
wait_for_mermaid
- # From https://github.com/mermaid-js/mermaid/blob/d3f8f03a7d03a052e1fe0251d5a6d8d1f48d67ee/src/dagre-wrapper/createLabel.js#L79-L82
- expected = %(<div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">Line 1<br>Line 2</div>)
+ # From # From https://github.com/mermaid-js/mermaid/blob/170ed89e9ef3e33dc84f8656eed1725379d505df/src/dagre-wrapper/createLabel.js#L39-L42
+ expected = %(<div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml">Line 1<br>Line 2</div>)
expect(page.html.scan(expected).count).to be(4)
end
@@ -70,8 +73,8 @@ RSpec.describe 'Mermaid rendering', :js do
wait_for_requests
wait_for_mermaid
- # From https://github.com/mermaid-js/mermaid/blob/d3f8f03a7d03a052e1fe0251d5a6d8d1f48d67ee/src/dagre-wrapper/createLabel.js#L79-L82
- expected = %(<div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">CLICK_HERE_AND_GET_BONUS</div>)
+ # From https://github.com/mermaid-js/mermaid/blob/170ed89e9ef3e33dc84f8656eed1725379d505df/src/dagre-wrapper/createLabel.js#L39-L42
+ expected = %(<div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml">CLICK_HERE_AND_GET_BONUS</div>)
expect(page.html).to include(expected)
end
@@ -300,6 +303,40 @@ RSpec.describe 'Mermaid rendering', :js do
expect(page).not_to have_xpath("//iframe")
end
end
+
+ it 'correctly copies and pastes to/from the clipboard' do
+ stub_feature_flags(sandboxed_mermaid: true)
+
+ description = <<~MERMAID
+ ```mermaid
+ graph TD;
+ A-->B;
+ A-->C;
+ ```
+ MERMAID
+
+ issue = create(:issue, project: project, description: description)
+
+ user = create(:user)
+ sign_in(user)
+ visit project_issue_path(project, issue)
+
+ wait_for_requests
+ wait_for_mermaid
+
+ find('pre.language-mermaid').hover
+ find('copy-code button').click
+
+ sleep 2
+
+ find('#note-body').send_keys [modifier_key, 'v']
+
+ wait_for_requests
+
+ # The codefences do actually get included, but we can't get spec to pass
+ # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83202#note_880621264
+ expect(find('#note-body').value.strip).to eq("graph TD;\n A-->B;\n A-->C;")
+ end
end
def wait_for_mermaid
diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb
index eb98c7d5061..9b54d95be6b 100644
--- a/spec/features/merge_request/batch_comments_spec.rb
+++ b/spec/features/merge_request/batch_comments_spec.rb
@@ -146,6 +146,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
before do
find('.js-show-diff-settings').click
click_button 'Side-by-side'
+ find('.js-show-diff-settings').click
end
it 'adds draft comments to both sides' do
@@ -171,9 +172,8 @@ RSpec.describe 'Merge request > Batch comments', :js do
write_reply_to_discussion(button_text: 'Add comment now', resolve: true)
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
- expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -188,9 +188,8 @@ RSpec.describe 'Merge request > Batch comments', :js do
wait_for_requests
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
- expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
end
@@ -211,9 +210,8 @@ RSpec.describe 'Merge request > Batch comments', :js do
write_reply_to_discussion(button_text: 'Add comment now', unresolve: true)
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('1 unresolved thread')
- expect(page).not_to have_selector('.line-resolve-btn.is-active')
end
end
@@ -230,9 +228,8 @@ RSpec.describe 'Merge request > Batch comments', :js do
wait_for_requests
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('1 unresolved thread')
- expect(page).not_to have_selector('.line-resolve-btn.is-active')
end
end
end
diff --git a/spec/features/merge_request/close_reopen_report_toggle_spec.rb b/spec/features/merge_request/close_reopen_report_toggle_spec.rb
index 8a4277d87c9..dea9a10a4ec 100644
--- a/spec/features/merge_request/close_reopen_report_toggle_spec.rb
+++ b/spec/features/merge_request/close_reopen_report_toggle_spec.rb
@@ -28,7 +28,6 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
expect(container).to have_link("Close merge request")
expect(container).to have_link('Report abuse')
- expect(container).to have_text("Report merge requests that are abusive, inappropriate or spam.")
end
it 'links to Report Abuse' do
@@ -43,10 +42,12 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
let(:issuable) { create(:merge_request, :opened, source_project: project) }
it 'shows the `Edit` and `Mark as draft` buttons' do
+ click_button 'Toggle dropdown'
+
expect(container).to have_link('Edit')
expect(container).to have_link('Mark as draft')
- expect(container).not_to have_button('Report abuse')
- expect(container).not_to have_button('Close merge request')
+ expect(container).to have_link('Close merge request')
+ expect(container).to have_link('Report abuse')
expect(container).not_to have_link('Reopen merge request')
end
end
@@ -55,21 +56,24 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
let(:issuable) { create(:merge_request, :closed, source_project: project) }
it 'shows both the `Edit` and `Reopen` button' do
+ click_button 'Toggle dropdown'
+
expect(container).to have_link('Edit')
- expect(container).not_to have_button('Report abuse')
- expect(container).not_to have_button('Close merge request')
+ expect(container).to have_link('Report abuse')
expect(container).to have_link('Reopen merge request')
+ expect(container).not_to have_link('Close merge request')
end
context 'when the merge request author is the current user' do
let(:issuable) { create(:merge_request, :closed, source_project: project, author: user) }
it 'shows both the `Edit` and `Reopen` button' do
+ click_button 'Toggle dropdown'
+
expect(container).to have_link('Edit')
- expect(container).not_to have_link('Report abuse')
- expect(container).not_to have_selector('button.dropdown-toggle')
- expect(container).not_to have_button('Close merge request')
expect(container).to have_link('Reopen merge request')
+ expect(container).not_to have_link('Close merge request')
+ expect(container).not_to have_link('Report abuse')
end
end
end
diff --git a/spec/features/merge_request/merge_request_discussion_lock_spec.rb b/spec/features/merge_request/merge_request_discussion_lock_spec.rb
index 4e0265839f6..a7bc2a062af 100644
--- a/spec/features/merge_request/merge_request_discussion_lock_spec.rb
+++ b/spec/features/merge_request/merge_request_discussion_lock_spec.rb
@@ -8,59 +8,91 @@ RSpec.describe 'Merge Request Discussion Lock', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project, author: user) }
+ let(:moved_mr_sidebar_enabled) { false }
before do
+ stub_feature_flags(moved_mr_sidebar: moved_mr_sidebar_enabled)
sign_in(user)
end
- context 'when a user is a team member' do
- before do
- project.add_developer(user)
- end
+ context 'moved sidebar flag disabled' do
+ context 'when a user is a team member' do
+ before do
+ project.add_developer(user)
+ end
- context 'when the discussion is unlocked' do
- it 'the user can lock the merge_request' do
- visit project_merge_request_path(merge_request.project, merge_request)
+ context 'when the discussion is unlocked' do
+ it 'the user can lock the merge_request' do
+ visit project_merge_request_path(merge_request.project, merge_request)
+
+ expect(find('.issuable-sidebar')).to have_content('Unlocked')
- expect(find('.issuable-sidebar')).to have_content('Unlocked')
+ page.within('.issuable-sidebar') do
+ find('.lock-edit').click
+ click_button('Lock')
+ end
- page.within('.issuable-sidebar') do
- find('.lock-edit').click
- click_button('Lock')
+ expect(find('[data-testid="lock-status"]')).to have_content('Locked')
end
+ end
+
+ context 'when the discussion is locked' do
+ before do
+ merge_request.update_attribute(:discussion_locked, true)
+ visit project_merge_request_path(merge_request.project, merge_request)
+ end
+
+ it 'the user can unlock the merge_request' do
+ expect(find('.issuable-sidebar')).to have_content('Locked')
- expect(find('[data-testid="lock-status"]')).to have_content('Locked')
+ page.within('.issuable-sidebar') do
+ find('.lock-edit').click
+ click_button('Unlock')
+ end
+
+ expect(find('[data-testid="lock-status"]')).to have_content('Unlocked')
+ end
end
end
- context 'when the discussion is locked' do
- before do
- merge_request.update_attribute(:discussion_locked, true)
- visit project_merge_request_path(merge_request.project, merge_request)
- end
+ context 'when a user is not a team member' do
+ context 'when the discussion is unlocked' do
+ before do
+ visit project_merge_request_path(merge_request.project, merge_request)
+ end
- it 'the user can unlock the merge_request' do
- expect(find('.issuable-sidebar')).to have_content('Locked')
+ it 'the user can not lock the merge_request' do
+ expect(find('.issuable-sidebar')).to have_content('Unlocked')
+ expect(find('.issuable-sidebar')).not_to have_selector('.lock-edit')
+ end
+ end
- page.within('.issuable-sidebar') do
- find('.lock-edit').click
- click_button('Unlock')
+ context 'when the discussion is locked' do
+ before do
+ merge_request.update_attribute(:discussion_locked, true)
+ visit project_merge_request_path(merge_request.project, merge_request)
end
- expect(find('[data-testid="lock-status"]')).to have_content('Unlocked')
+ it 'the user can not unlock the merge_request' do
+ expect(find('.issuable-sidebar')).to have_content('Locked')
+ expect(find('.issuable-sidebar')).not_to have_selector('.lock-edit')
+ end
end
end
end
- context 'when a user is not a team member' do
+ context 'moved sidebar flag enabled' do
+ let(:moved_mr_sidebar_enabled) { true }
+
context 'when the discussion is unlocked' do
before do
visit project_merge_request_path(merge_request.project, merge_request)
end
- it 'the user can not lock the merge_request' do
- expect(find('.issuable-sidebar')).to have_content('Unlocked')
- expect(find('.issuable-sidebar')).not_to have_selector('.lock-edit')
+ it 'the user can lock the merge_request' do
+ click_button 'Toggle dropdown'
+
+ expect(page).to have_content('Lock merge request')
end
end
@@ -70,9 +102,10 @@ RSpec.describe 'Merge Request Discussion Lock', :js do
visit project_merge_request_path(merge_request.project, merge_request)
end
- it 'the user can not unlock the merge_request' do
- expect(find('.issuable-sidebar')).to have_content('Locked')
- expect(find('.issuable-sidebar')).not_to have_selector('.lock-edit')
+ it 'the user can unlock the merge_request' do
+ click_button 'Toggle dropdown'
+
+ expect(page).to have_content('Unlock merge request')
end
end
end
diff --git a/spec/features/merge_request/user_accepts_merge_request_spec.rb b/spec/features/merge_request/user_accepts_merge_request_spec.rb
index d4b185a82e9..159306b28d8 100644
--- a/spec/features/merge_request/user_accepts_merge_request_spec.rb
+++ b/spec/features/merge_request/user_accepts_merge_request_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
click_button('Merge')
- expect(page).to have_content("The changes were merged into #{merge_request.target_branch} with #{merge_request.short_merged_commit_sha}")
+ expect(page).to have_content("Changes merged into #{merge_request.target_branch} with #{merge_request.short_merged_commit_sha}")
end
context 'when merge method is set to fast-forward merge' do
@@ -31,7 +31,7 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
click_button('Merge')
- expect(page).to have_content("The changes were merged into #{merge_request.target_branch} with #{merge_request.short_merged_commit_sha}")
+ expect(page).to have_content("Changes merged into #{merge_request.target_branch} with #{merge_request.short_merged_commit_sha}")
end
it 'accepts a merge request with squash and merge' do
@@ -41,7 +41,7 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
click_button('Merge')
- expect(page).to have_content("The changes were merged into #{merge_request.target_branch} with #{merge_request.short_merged_commit_sha}")
+ expect(page).to have_content("Changes merged into #{merge_request.target_branch} with #{merge_request.short_merged_commit_sha}")
end
end
end
@@ -55,7 +55,7 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
check('Delete source branch')
click_button('Merge')
- expect(page).to have_content('The changes were merged into')
+ expect(page).to have_content('Changes merged into')
expect(page).not_to have_selector('.js-remove-branch-button')
# Wait for View Resource requests to complete so they don't blow up if they are
@@ -72,7 +72,7 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
it 'accepts a merge request' do
click_button('Merge')
- expect(page).to have_content('The changes were merged into')
+ expect(page).to have_content('Changes merged into')
expect(page).to have_selector('.js-remove-branch-button')
# Wait for View Resource requests to complete so they don't blow up if they are
@@ -90,7 +90,7 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
check('Delete source branch')
click_button('Merge')
- expect(page).to have_content('The changes were merged into')
+ expect(page).to have_content('Changes merged into')
expect(page).not_to have_selector('.js-remove-branch-button')
# Wait for View Resource requests to complete so they don't blow up if they are
@@ -107,14 +107,12 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
end
it 'accepts a merge request' do
- find('.js-mr-widget-commits-count').click
+ find('[data-testid="widget_edit_commit_message"]').click
fill_in('merge-message-edit', with: 'wow such merge')
click_button('Merge')
- page.within('.status-box') do
- expect(page).to have_content('Merged')
- end
+ expect(page).to have_selector('.gl-badge', text: 'Merged')
end
end
end
diff --git a/spec/features/merge_request/user_assigns_themselves_spec.rb b/spec/features/merge_request/user_assigns_themselves_spec.rb
index fc925781a3b..2aaddc7791b 100644
--- a/spec/features/merge_request/user_assigns_themselves_spec.rb
+++ b/spec/features/merge_request/user_assigns_themselves_spec.rb
@@ -30,12 +30,6 @@ RSpec.describe 'Merge request > User assigns themselves' do
end.to change { merge_request.reload.updated_at }
end
- it 'returns user to the merge request', :js do
- click_link 'Assign yourself to these issues'
-
- expect(page).to have_content merge_request.description
- end
-
context 'when related issues are already assigned' do
before do
[issue1, issue2].each { |issue| issue.update!(assignees: [user]) }
diff --git a/spec/features/merge_request/user_awards_emoji_spec.rb b/spec/features/merge_request/user_awards_emoji_spec.rb
index 35eadb34799..81a88cad458 100644
--- a/spec/features/merge_request/user_awards_emoji_spec.rb
+++ b/spec/features/merge_request/user_awards_emoji_spec.rb
@@ -38,6 +38,10 @@ RSpec.describe 'Merge request > User awards emoji', :js do
it 'adds awards to note' do
page.within('.note-actions') do
first('.note-emoji-button').click
+
+ # make sure emoji popup is visible
+ execute_script("window.scrollBy(0, 200)")
+
find('gl-emoji[data-name="8ball"]').click
end
diff --git a/spec/features/merge_request/user_comments_on_diff_spec.rb b/spec/features/merge_request/user_comments_on_diff_spec.rb
index c06019f6b77..99756da51e4 100644
--- a/spec/features/merge_request/user_comments_on_diff_spec.rb
+++ b/spec/features/merge_request/user_comments_on_diff_spec.rb
@@ -103,15 +103,22 @@ RSpec.describe 'User comments on a diff', :js do
end
# Check the same comments in the side-by-side view.
+ execute_script "window.scrollTo(0,0)"
find('.js-show-diff-settings').click
click_button 'Side-by-side'
+ second_line_element = find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']")
+ second_root_element = second_line_element.ancestor('[data-path]')
+
wait_for_requests
page.within(second_root_element) do
expect(page).to have_content('Line is wrong')
end
+ first_line_element = find_by_scrolling("[id='#{sample_compare.changes[0][:line_code]}']").find(:xpath, "..")
+ first_root_element = first_line_element.ancestor('[data-path]')
+
page.within(first_root_element) do
expect(page).to have_content('Line is correct')
end
@@ -154,7 +161,7 @@ RSpec.describe 'User comments on a diff', :js do
it 'allows comments on previously hidden lines the middle of a file' do
# Click 27, expand up, select 18, add and verify comment
page.within find_by_scrolling('[data-path="files/ruby/popen.rb"]') do
- all('.js-unfold-all')[1].click
+ first('.js-unfold-all').click
end
click_diff_line(find('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="21"]').find(:xpath, '../..'), 'left')
add_comment('18', '21')
@@ -163,10 +170,10 @@ RSpec.describe 'User comments on a diff', :js do
it 'allows comments on previously hidden lines at the bottom of a file' do
# Click +28, expand down, select 37 add and verify comment
page.within find_by_scrolling('[data-path="files/ruby/popen.rb"]') do
- all('.js-unfold-down:not([disabled])')[1].click
+ first('.js-unfold-down').click
end
click_diff_line(find('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="30"]').find(:xpath, '../..'), 'left')
- add_comment('+28', '37')
+ add_comment('+28', '30')
end
end
diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
index 15f186b649a..bd5048374d5 100644
--- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
@@ -258,7 +258,7 @@ RSpec.describe 'Merge request > User creates image diff notes', :js do
end
it 'resizes image' do
- expect(find('.onion-skin-frame')['style']).to match('width: 228px; height: 240px;')
+ expect(find('.onion-skin-frame')['style']).to match('width: 198px; height: 210px;')
end
it_behaves_like 'onion skin'
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 617aceae54c..a3dc3079374 100644
--- a/spec/features/merge_request/user_creates_merge_request_spec.rb
+++ b/spec/features/merge_request/user_creates_merge_request_spec.rb
@@ -15,28 +15,40 @@ RSpec.describe "User creates a merge request", :js do
sign_in(user)
end
- it "creates a merge request" do
- visit(project_new_merge_request_path(project))
+ context 'when completed the compare branches form' do
+ before do
+ visit(project_new_merge_request_path(project))
- find(".js-source-branch").click
- click_link("fix")
+ find(".js-source-branch").click
+ click_link("fix")
- find(".js-target-branch").click
- click_link("feature")
+ find(".js-target-branch").click
+ click_link("feature")
- click_button("Compare branches")
+ click_button("Compare branches")
+ end
- page.within('.merge-request-form') do
- expect(page.find('#merge_request_title')['placeholder']).to eq 'Title'
- expect(page.find('#merge_request_description')['placeholder']).to eq 'Describe the goal of the changes and what reviewers should be aware of.'
+ it "shows merge request form" do
+ page.within('.merge-request-form') do
+ expect(page.find('#merge_request_title')['placeholder']).to eq 'Title'
+ expect(page.find('#merge_request_description')['placeholder']).to eq 'Describe the goal of the changes and what reviewers should be aware of.'
+ end
end
- fill_in("Title", with: title)
- click_button("Create merge request")
+ context "when completed the merge request form" do
+ before do
+ fill_in("Title", with: title)
+ click_button("Create merge request")
+ end
- page.within(".merge-request") do
- expect(page).to have_content(title)
+ it "creates a merge request" do
+ page.within(".merge-request") do
+ expect(page).to have_content(title)
+ end
+ end
end
+
+ it_behaves_like 'merge request author auto assign'
end
context "XSS branch name exists" do
@@ -106,7 +118,7 @@ RSpec.describe "User creates a merge request", :js do
click_button("Create merge request")
- expect(page).to have_content(title).and have_content("Request to merge #{user.namespace.path}:#{source_branch} into master")
+ expect(page).to have_content(title).and have_content("requested to merge #{forked_project.full_path}:#{source_branch} into master")
end
end
end
diff --git a/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
index 67a232607cd..059e1eb89c5 100644
--- a/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
+++ b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do
it 'has commit message without description' do
expect(page).not_to have_selector('#merge-message-edit')
- first('.js-mr-widget-commits-count').click
+ find('[data-testid="widget_edit_commit_message"]').click
expect(merge_textbox).to be_visible
expect(merge_textbox.value).to eq(default_merge_commit_message)
end
@@ -51,7 +51,7 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do
it 'uses merge commit template' do
expect(page).not_to have_selector('#merge-message-edit')
- first('.js-mr-widget-commits-count').click
+ find('[data-testid="widget_edit_commit_message"]').click
expect(merge_textbox).to be_visible
expect(merge_textbox.value).to eq(merge_request.title)
end
@@ -62,7 +62,7 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do
it 'has default message with merge request title' do
expect(page).not_to have_selector('#squash-message-edit')
- first('.js-mr-widget-commits-count').click
+ find('[data-testid="widget_edit_commit_message"]').click
expect(squash_textbox).to be_visible
expect(merge_textbox).to be_visible
expect(squash_textbox.value).to eq(merge_request.title)
@@ -74,7 +74,7 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do
it 'uses squash commit template' do
expect(page).not_to have_selector('#squash-message-edit')
- first('.js-mr-widget-commits-count').click
+ find('[data-testid="widget_edit_commit_message"]').click
expect(squash_textbox).to be_visible
expect(merge_textbox).to be_visible
expect(squash_textbox.value).to eq(merge_request.description)
diff --git a/spec/features/merge_request/user_edits_merge_request_spec.rb b/spec/features/merge_request/user_edits_merge_request_spec.rb
index 364af8d8a76..0b4b9d7452a 100644
--- a/spec/features/merge_request/user_edits_merge_request_spec.rb
+++ b/spec/features/merge_request/user_edits_merge_request_spec.rb
@@ -92,7 +92,7 @@ RSpec.describe 'User edits a merge request', :js do
select2('merge-test', from: '#merge_request_target_branch')
click_button('Save changes')
- expect(page).to have_content("Request to merge #{merge_request.source_branch} into merge-test")
+ expect(page).to have_content("requested to merge #{merge_request.source_branch} into merge-test")
expect(page).to have_content("changed target branch from #{merge_request.target_branch} to merge-test")
end
diff --git a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
index 7d67cde4bbb..f5b5460769e 100644
--- a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
+++ b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
@@ -59,7 +59,6 @@ RSpec.describe 'Batch diffs', :js do
# Confirm scrolled to correct UI element
expect(get_first_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_falsey
- expect(get_second_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_truthy
end
end
diff --git a/spec/features/merge_request/user_manages_subscription_spec.rb b/spec/features/merge_request/user_manages_subscription_spec.rb
index 3cdb22000f6..c64c761b8d1 100644
--- a/spec/features/merge_request/user_manages_subscription_spec.rb
+++ b/spec/features/merge_request/user_manages_subscription_spec.rb
@@ -6,29 +6,60 @@ RSpec.describe 'User manages subscription', :js do
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:user) { create(:user) }
+ let(:moved_mr_sidebar_enabled) { false }
before do
+ stub_feature_flags(moved_mr_sidebar: moved_mr_sidebar_enabled)
+
project.add_maintainer(user)
sign_in(user)
visit(merge_request_path(merge_request))
end
- it 'toggles subscription' do
- page.within('[data-testid="subscription-toggle"]') do
+ context 'moved sidebar flag disabled' do
+ it 'toggles subscription' do
+ page.within('[data-testid="subscription-toggle"]') do
+ wait_for_requests
+
+ expect(page).to have_css 'button:not(.is-checked)'
+ find('button:not(.is-checked)').click
+
+ wait_for_requests
+
+ expect(page).to have_css 'button.is-checked'
+ find('button.is-checked').click
+
+ wait_for_requests
+
+ expect(page).to have_css 'button:not(.is-checked)'
+ end
+ end
+ end
+
+ context 'moved sidebar flag enabled' do
+ let(:moved_mr_sidebar_enabled) { true }
+
+ it 'toggles subscription' do
wait_for_requests
- expect(page).to have_css 'button:not(.is-checked)'
- find('button:not(.is-checked)').click
+ click_button 'Toggle dropdown'
+
+ expect(page).to have_content('Turn on notifications')
+ click_button 'Turn on notifications'
wait_for_requests
- expect(page).to have_css 'button.is-checked'
- find('button.is-checked').click
+ click_button 'Toggle dropdown'
+
+ expect(page).to have_content('Turn off notifications')
+ click_button 'Turn off notifications'
wait_for_requests
- expect(page).to have_css 'button:not(.is-checked)'
+ click_button 'Toggle dropdown'
+
+ expect(page).to have_content('Turn on notifications')
end
end
end
diff --git a/spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb b/spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb
index f5bca7cf015..c3a61476442 100644
--- a/spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb
+++ b/spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb
@@ -16,10 +16,13 @@ RSpec.describe 'Merge request > User marks merge request as draft', :js do
end
it 'toggles draft status' do
+ click_button 'Toggle dropdown'
click_link 'Mark as draft'
expect(page).to have_content("Draft: #{merge_request.title}")
+ click_button 'Toggle dropdown'
+
page.within('.detail-page-header-actions') do
click_link 'Mark as ready'
end
diff --git a/spec/features/merge_request/user_merges_immediately_spec.rb b/spec/features/merge_request/user_merges_immediately_spec.rb
index 3a05f35a671..91327059e0f 100644
--- a/spec/features/merge_request/user_merges_immediately_spec.rb
+++ b/spec/features/merge_request/user_merges_immediately_spec.rb
@@ -30,17 +30,17 @@ RSpec.describe 'Merge requests > User merges immediately', :js do
it 'enables merge immediately' do
wait_for_requests
- page.within '.mr-widget-body' do
+ page.within '[data-testid="ready_to_merge_state"]' do
find('.dropdown-toggle').click
Sidekiq::Testing.fake! do
click_button 'Merge immediately'
-
- expect(find('.media-body h4')).to have_content('Merging!')
-
- wait_for_requests
end
end
+
+ expect(find('.media-body h4')).to have_content('Merging!')
+
+ wait_for_requests
end
end
end
diff --git a/spec/features/merge_request/user_merges_merge_request_spec.rb b/spec/features/merge_request/user_merges_merge_request_spec.rb
index a861ca2eea5..6a9a30953df 100644
--- a/spec/features/merge_request/user_merges_merge_request_spec.rb
+++ b/spec/features/merge_request/user_merges_merge_request_spec.rb
@@ -17,9 +17,7 @@ RSpec.describe "User merges a merge request", :js do
click_button("Merge")
end
- page.within(".status-box") do
- expect(page).to have_content("Merged")
- end
+ expect(page).to have_selector('.gl-badge', text: 'Merged')
end
end
@@ -27,6 +25,7 @@ RSpec.describe "User merges a merge request", :js do
let(:project) { create(:project, :public, :repository, merge_requests_ff_only_enabled: true) }
before do
+ stub_feature_flags(restructured_mr_widget: false)
visit(merge_request_path(merge_request))
end
diff --git a/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
index 4d7ee11e366..d6b132b18da 100644
--- a/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
@@ -56,7 +56,7 @@ RSpec.describe 'Merge request > User merges only if pipeline succeeds', :js do
wait_for_requests
- expect(page).to have_css('button[disabled="disabled"]', text: 'Merge')
+ expect(page).not_to have_button('Merge')
expect(page).to have_content('Merge blocked: pipeline must succeed. Push a commit that fixes the failure, or learn about other solutions.')
end
end
diff --git a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
index 9057b96bff0..21f96299958 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
@@ -36,7 +36,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do
click_button "Merge when pipeline succeeds"
expect(page).to have_content "Set by #{user.name} to be merged automatically when the pipeline succeeds"
- expect(page).to have_content "Does not delete the source branch"
+ expect(page).to have_content "Source branch will not be deleted"
expect(page).to have_selector ".js-cancel-auto-merge"
visit project_merge_request_path(project, merge_request) # Needed to refresh the page
expect(page).to have_content /enabled an automatic merge when the pipeline for \h{8} succeeds/i
@@ -64,6 +64,9 @@ 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"
+
+ wait_for_requests
+
click_button "Cancel auto-merge"
wait_for_requests
@@ -123,12 +126,6 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do
expect(page).to have_content "canceled the automatic merge"
end
- it 'allows to delete source branch' do
- click_button "Delete source branch"
-
- expect(page).to have_content "Deletes the source branch"
- end
-
context 'when pipeline succeeds' do
before do
build.success
@@ -136,7 +133,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do
end
it 'merges merge request', :sidekiq_might_not_need_inline do
- expect(page).to have_content 'The changes were merged'
+ expect(page).to have_content 'Changes merged'
expect(merge_request.reload).to be_merged
end
end
diff --git a/spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb b/spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb
new file mode 100644
index 00000000000..4d2c59665bb
--- /dev/null
+++ b/spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Merge request > User opens checkout branch modal', :js do
+ include ProjectForksHelper
+
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ describe 'for fork' do
+ let(:author) { create(:user) }
+ let(:source_project) { fork_project(project, author, repository: true) }
+
+ let(:merge_request) do
+ create(:merge_request,
+ source_project: source_project,
+ target_project: project,
+ source_branch: 'fix',
+ target_branch: 'master',
+ author: author,
+ allow_collaboration: true)
+ end
+
+ it 'shows instructions' do
+ visit project_merge_request_path(project, merge_request)
+
+ click_button 'Code'
+ click_button 'Check out branch'
+
+ expect(page).to have_content(source_project.http_url_to_repo)
+ end
+ end
+end
diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb
index d803aec5895..64715f9234a 100644
--- a/spec/features/merge_request/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb
@@ -239,7 +239,7 @@ RSpec.describe 'Merge request > User posts diff notes', :js do
def should_allow_dismissing_a_comment(line_holder, diff_side = nil)
write_comment_on_line(line_holder, diff_side)
- accept_gl_confirm(s_('Notes|Are you sure you want to cancel creating this comment?')) do
+ accept_gl_confirm(s_('Notes|Are you sure you want to cancel creating this comment?'), button_text: _('Discard changes')) do
find('.js-close-discussion-note-form').click
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 231722c166d..e09ec11f095 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
@@ -38,7 +38,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
context 'single thread' do
it 'shows text with how many threads' do
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('1 unresolved thread')
end
end
@@ -55,9 +55,8 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
expect(page).to have_selector('.btn', text: 'Unresolve thread')
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
- expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -72,9 +71,8 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
expect(page).to have_selector('.line-resolve-btn.is-active')
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
- expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -84,7 +82,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
click_button 'Unresolve thread'
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('1 unresolved thread')
end
end
@@ -155,7 +153,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
wait_for_requests
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
end
end
@@ -167,9 +165,8 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
wait_for_requests
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('1 unresolved thread')
- expect(page).not_to have_selector('.line-resolve-btn.is-active')
end
end
@@ -184,7 +181,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
wait_for_requests
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('1 unresolved thread')
end
end
@@ -196,9 +193,8 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
- expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -213,14 +209,13 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
click_button 'Add comment now'
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
- expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
it 'allows user to quickly scroll to next unresolved thread' do
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
page.find('.discussion-next-btn').click
end
@@ -269,7 +264,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
expect(first('.line-resolve-btn')['aria-label']).to eq("Resolved by #{user.name}")
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
end
end
@@ -286,7 +281,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
expect(button['aria-label']).to eq("Resolved by #{user.name}")
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
end
end
@@ -299,7 +294,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
end
it 'shows text with how many threads' do
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('2 unresolved threads')
end
end
@@ -307,7 +302,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to mark a single note as resolved' do
click_button('Resolve thread', match: :first)
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('1 unresolved thread')
end
end
@@ -317,9 +312,8 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
btn.click
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
- expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -330,9 +324,8 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
end
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
- expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -341,7 +334,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
page.find('.discussion-next-btn').click
end
@@ -370,7 +363,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
expect(page).not_to have_selector('.btn', text: 'Resolve thread')
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
page.find('.discussion-next-btn').click
end
@@ -386,7 +379,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
context 'changes tab' do
it 'shows text with how many threads' do
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('1 unresolved thread')
end
end
@@ -402,9 +395,8 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
expect(page).to have_selector('.btn', text: 'Unresolve thread')
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
- expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -417,9 +409,8 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
expect(page).to have_selector('.line-resolve-btn.is-active')
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
- expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -429,7 +420,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
click_button 'Unresolve thread'
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('1 unresolved thread')
end
end
@@ -445,9 +436,8 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
click_button 'Add comment now'
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
- expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -462,7 +452,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
click_button 'Add comment now'
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('1 unresolved thread')
end
end
@@ -485,7 +475,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
expect(page).not_to have_selector('.line-resolve-btn')
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('1 unresolved thread')
end
end
@@ -515,9 +505,8 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
expect(page).to have_selector('.btn', text: 'Unresolve thread')
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('All threads resolved')
- expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
end
@@ -534,32 +523,11 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
expect(page).not_to have_selector('.line-resolve-btn')
end
- page.within '.line-resolve-all-container' do
+ page.within '.discussions-counter' do
expect(page).to have_content('1 unresolved thread')
end
end
end
-
- context 'resolved comment' do
- before do
- note.resolve!(user)
- visit_merge_request
- end
-
- it 'shows resolved icon' do
- expect(page).to have_content 'All threads resolved'
-
- click_button _('Show thread')
- expect(page).to have_selector('.line-resolve-btn.is-active')
- end
-
- it 'does not allow user to click resolve button' do
- expect(page).to have_selector('.line-resolve-btn.is-active')
- click_button _('Show thread')
-
- expect(page).to have_selector('.line-resolve-btn.is-active')
- end
- end
end
def visit_merge_request(mr = nil)
diff --git a/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
index 38546fd629d..5827266d4b7 100644
--- a/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
+++ b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
@@ -11,6 +11,8 @@ RSpec.describe 'Merge request > User sees check out branch modal', :js do
sign_in(user)
visit project_merge_request_path(project, merge_request)
wait_for_requests
+
+ click_button 'Code'
click_button('Check out branch')
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 c9b21d4a4ae..2dafd66b406 100644
--- a/spec/features/merge_request/user_sees_merge_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -20,6 +20,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
stub_feature_flags(refactor_mr_widgets_extensions: false)
stub_feature_flags(refactor_mr_widgets_extensions_user: false)
+ stub_feature_flags(refactor_mr_widget_test_summary: false)
end
context 'new merge request', :sidekiq_might_not_need_inline do
@@ -320,8 +321,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
# Wait for the `ci_status` and `merge_check` requests
wait_for_requests
- expect(page).to have_selector('.accept-merge-request')
- expect(find('.accept-merge-request')['disabled']).not_to be(true)
+ expect(page).not_to have_selector('.accept-merge-request')
end
end
@@ -384,9 +384,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
# Wait for the `ci_status` and `merge_check` requests
wait_for_requests
- page.within('.mr-widget-body') do
- expect(page).to have_content('Merge Merge blocked: fast-forward merge is not possible. To merge this request, first rebase locally.')
- end
+ expect(page).to have_content('Merge blocked: fast-forward merge is not possible. To merge this request, first rebase locally.')
end
end
@@ -444,7 +442,6 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
it 'user cannot remove source branch', :sidekiq_might_not_need_inline do
expect(page).not_to have_field('remove-source-branch-input')
- expect(page).to have_content('Deletes the source branch')
end
end
diff --git a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
index 23b03e33f5d..03f9f6ef565 100644
--- a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
+++ b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
@@ -61,38 +61,6 @@ RSpec.describe 'Merge request < User sees mini pipeline graph', :js do
wait_for_requests
end
- # Status icon button styles should update as described in
- # https://gitlab.com/gitlab-org/gitlab-foss/issues/42769
- it 'has unique styles for default, :hover, :active, and :focus states' do
- default_background_color, default_foreground_color, default_box_shadow = get_toggle_colors(dropdown_selector)
-
- toggle.hover
- hover_background_color, hover_foreground_color, hover_box_shadow = get_toggle_colors(dropdown_selector)
-
- page.driver.browser.action.click_and_hold(toggle.native).perform
- active_background_color, active_foreground_color, active_box_shadow = get_toggle_colors(dropdown_selector)
- page.driver.browser.action.release(toggle.native).perform
-
- page.driver.browser.action.click(toggle.native).move_by(100, 100).perform
- focus_background_color, focus_foreground_color, focus_box_shadow = get_toggle_colors(dropdown_selector)
-
- expect(default_background_color).not_to eq(hover_background_color)
- expect(hover_background_color).not_to eq(active_background_color)
- expect(default_background_color).not_to eq(active_background_color)
-
- expect(default_foreground_color).not_to eq(hover_foreground_color)
- expect(hover_foreground_color).not_to eq(active_foreground_color)
- expect(default_foreground_color).not_to eq(active_foreground_color)
-
- expect(focus_background_color).to eq(hover_background_color)
- expect(focus_foreground_color).to eq(hover_foreground_color)
-
- expect(default_box_shadow).to eq('none')
- expect(hover_box_shadow).to eq('none')
- expect(active_box_shadow).not_to eq('none')
- expect(focus_box_shadow).not_to eq('none')
- end
-
it 'shows tooltip when hovered' do
toggle.hover
@@ -147,15 +115,4 @@ RSpec.describe 'Merge request < User sees mini pipeline graph', :js do
end
end
end
-
- private
-
- def get_toggle_colors(selector)
- find(selector)
- [
- evaluate_script("$('#{selector} button:visible').css('background-color');"),
- evaluate_script("$('#{selector} button:visible svg').css('fill');"),
- evaluate_script("$('#{selector} button:visible').css('box-shadow');")
- ]
- end
end
diff --git a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
index d2bde320c54..bcc6abd4f65 100644
--- a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
+++ b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
@@ -62,6 +62,7 @@ RSpec.describe 'Merge request > User selects branches for new MR', :js do
fill_in "merge_request_title", with: "Orphaned MR test"
click_button "Create merge request"
+ click_button 'Code'
click_button "Check out branch"
expect(page).to have_content 'git checkout -b \'orphaned-branch\' \'origin/orphaned-branch\''
diff --git a/spec/features/merge_request/user_squashes_merge_request_spec.rb b/spec/features/merge_request/user_squashes_merge_request_spec.rb
index 2a48657ac4f..da0d4ca23d1 100644
--- a/spec/features/merge_request/user_squashes_merge_request_spec.rb
+++ b/spec/features/merge_request/user_squashes_merge_request_spec.rb
@@ -79,7 +79,7 @@ RSpec.describe 'User squashes a merge request', :js do
context 'when squash message is the same as existing commit message' do
before do
- click_button("Modify commit messages")
+ find('[data-testid="widget_edit_commit_message"]').click
fill_in('Squash commit message', with: project.commit(source_branch).safe_message)
accept_mr
end
diff --git a/spec/features/merge_request/user_views_diffs_spec.rb b/spec/features/merge_request/user_views_diffs_spec.rb
index 208ed1f01e7..894292c99eb 100644
--- a/spec/features/merge_request/user_views_diffs_spec.rb
+++ b/spec/features/merge_request/user_views_diffs_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe 'User views diffs', :js do
it 'unfolds diffs in the middle' do
page.within('.file-holder[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd"]') do
- all('.js-unfold-all')[1].click
+ first('.js-unfold-all').click
expect(page).to have_selector('[data-interop-type="new"] [data-linenumber="24"]', count: 1)
expect(page).not_to have_selector('[data-interop-type="new"] [data-linenumber="1"]')
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 a145bcb976b..1f4682b4a46 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
@@ -71,13 +71,14 @@ RSpec.describe 'User views an open merge request' do
let(:merge_request) { create(:merge_request, :rebased, source_project: project, target_project: project) }
before do
+ project.add_maintainer(project.creator)
+ sign_in(project.creator)
+
visit(merge_request_path(merge_request))
end
it 'does not show diverged commits count' do
- page.within('.mr-source-target') do
- expect(page).not_to have_content(/([0-9]+ commits? behind)/)
- end
+ expect(page).not_to have_content(/([0-9]+ commits? behind)/)
end
end
@@ -85,13 +86,14 @@ RSpec.describe 'User views an open merge request' do
let(:merge_request) { create(:merge_request, :diverged, source_project: project, target_project: project) }
before do
+ project.add_maintainer(project.creator)
+ sign_in(project.creator)
+
visit(merge_request_path(merge_request))
end
it 'shows diverged commits count' do
- page.within('.mr-source-target') do
- expect(page).to have_content(/([0-9]+ commits behind)/)
- end
+ expect(page).not_to have_content(/([0-9]+ commits? behind)/)
end
end
@@ -117,6 +119,8 @@ RSpec.describe 'User views an open merge request' do
let(:source_branch) { "&#39;&gt;&lt;iframe/srcdoc=&#39;&#39;&gt;&lt;/iframe&gt;" }
before do
+ stub_feature_flags(moved_mr_sidebar: false)
+
project.repository.create_branch(source_branch, "master")
mr = create(:merge_request, source_project: project, target_project: project, source_branch: source_branch)
diff --git a/spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb b/spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb
index a6de443e96f..f637186ec67 100644
--- a/spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb
+++ b/spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb
@@ -11,6 +11,10 @@ RSpec.describe 'Project > Merge request > View user status' do
subject { visit merge_request_path(merge_request) }
describe 'the status of the merge request author' do
+ before do
+ stub_feature_flags(updated_mr_header: false)
+ end
+
it_behaves_like 'showing user status' do
let(:user_with_status) { merge_request.author }
end
diff --git a/spec/features/merge_requests/user_lists_merge_requests_spec.rb b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
index 8c1d9dd38b0..2743f7e8ed4 100644
--- a/spec/features/merge_requests/user_lists_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
@@ -109,7 +109,7 @@ RSpec.describe 'Merge requests > User lists merge requests' do
expect(count_merge_requests).to eq(4)
end
- it 'sorts by milestone' do
+ it 'sorts by milestone due date' do
visit_merge_requests(project, sort: sort_value_milestone)
expect(first_merge_request).to include('fix')
@@ -130,12 +130,12 @@ RSpec.describe 'Merge requests > User lists merge requests' do
expect(count_merge_requests).to eq(4)
end
- it 'filters on one label and sorts by due date' do
+ it 'filters on one label and sorts by milestone due date' do
label = create(:label, project: project)
create(:label_link, label: label, target: @fix)
visit_merge_requests(project, label_name: [label.name],
- sort: sort_value_due_date)
+ sort: sort_value_milestone)
expect(first_merge_request).to include('fix')
expect(count_merge_requests).to eq(1)
@@ -150,19 +150,19 @@ RSpec.describe 'Merge requests > User lists merge requests' do
create(:label_link, label: label2, target: @fix)
end
- it 'sorts by due date' do
+ it 'sorts by milestone due date' do
visit_merge_requests(project, label_name: [label.name, label2.name],
- sort: sort_value_due_date)
+ sort: sort_value_milestone)
expect(first_merge_request).to include('fix')
expect(count_merge_requests).to eq(1)
end
context 'filter on assignee and' do
- it 'sorts by due soon' do
+ it 'sorts by milestone due date' do
visit_merge_requests(project, label_name: [label.name, label2.name],
assignee_id: user.id,
- sort: sort_value_due_date)
+ sort: sort_value_milestone)
expect(first_merge_request).to include('fix')
expect(count_merge_requests).to eq(1)
diff --git a/spec/features/merge_requests/user_mass_updates_spec.rb b/spec/features/merge_requests/user_mass_updates_spec.rb
index a15b6072e70..fa866beb773 100644
--- a/spec/features/merge_requests/user_mass_updates_spec.rb
+++ b/spec/features/merge_requests/user_mass_updates_spec.rb
@@ -5,12 +5,14 @@ require 'spec_helper'
RSpec.describe 'Merge requests > User mass updates', :js do
let(:project) { create(:project, :repository) }
let(:user) { project.creator }
+ let(:user2) { create(:user) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
before do
stub_feature_flags(mr_attention_requests: false)
project.add_maintainer(user)
+ project.add_maintainer(user2)
sign_in(user)
end
@@ -68,9 +70,9 @@ RSpec.describe 'Merge requests > User mass updates', :js do
end
it 'updates merge request with assignee' do
- change_assignee(user.name)
+ change_assignee(user2.name)
- expect(find('.issuable-meta a.author-link')[:title]).to eq "Attention requested from assignee #{user.name}"
+ expect(find('.issuable-meta a.author-link')[:title]).to eq "Attention requested from assignee #{user2.name}"
end
end
end
diff --git a/spec/features/merge_requests/user_sorts_merge_requests_spec.rb b/spec/features/merge_requests/user_sorts_merge_requests_spec.rb
index 99473f3b1ea..459145d3ef0 100644
--- a/spec/features/merge_requests/user_sorts_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_sorts_merge_requests_spec.rb
@@ -2,8 +2,9 @@
require 'spec_helper'
-RSpec.describe 'User sorts merge requests' do
+RSpec.describe 'User sorts merge requests', :js do
include CookieHelper
+ include Spec::Support::Helpers::Features::SortingHelpers
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let!(:merge_request2) do
@@ -16,29 +17,27 @@ RSpec.describe 'User sorts merge requests' do
let_it_be(:project) { create(:project, :public, group: group) }
before do
+ stub_feature_flags(vue_issues_list: true)
+
sign_in(user)
visit(project_merge_requests_path(project))
end
it 'keeps the sort option' do
- find('.filter-dropdown-container .dropdown').click
-
- page.within('ul.dropdown-menu.dropdown-menu-right li') do
- click_link('Milestone')
- end
+ pajamas_sort_by(s_('SortOptions|Milestone'))
visit(merge_requests_dashboard_path(assignee_username: user.username))
- expect(find('.issues-filters a.is-active')).to have_content('Milestone')
+ expect(find('.filter-dropdown-container button.dropdown-toggle')).to have_content('Milestone')
visit(project_merge_requests_path(project))
- expect(find('.issues-filters a.is-active')).to have_content('Milestone')
+ expect(find('.filter-dropdown-container button.dropdown-toggle')).to have_content('Milestone')
visit(merge_requests_group_path(group))
- expect(find('.issues-filters a.is-active')).to have_content('Milestone')
+ expect(find('.filter-dropdown-container button.dropdown-toggle')).to have_content('Milestone')
end
it 'fallbacks to issuable_sort cookie key when remembering the sorting option' do
@@ -46,17 +45,13 @@ RSpec.describe 'User sorts merge requests' do
visit(merge_requests_dashboard_path(assignee_username: user.username))
- expect(find('.issues-filters a.is-active')).to have_content('Milestone')
+ expect(find('.filter-dropdown-container button.dropdown-toggle')).to have_content('Milestone')
end
- it 'separates remember sorting with issues' do
+ it 'separates remember sorting with issues', :js do
create(:issue, project: project)
- find('.filter-dropdown-container .dropdown').click
-
- page.within('ul.dropdown-menu.dropdown-menu-right li') do
- click_link('Milestone')
- end
+ pajamas_sort_by(s_('SortOptions|Milestone'))
visit(project_issues_path(project))
@@ -73,11 +68,7 @@ RSpec.describe 'User sorts merge requests' do
end
it 'sorts by popularity' do
- find('.filter-dropdown-container .dropdown').click
-
- page.within('ul.dropdown-menu.dropdown-menu-right li') do
- click_link('Popularity')
- end
+ pajamas_sort_by(s_('SortOptions|Popularity'))
page.within('.mr-list') do
page.within('li.merge-request:nth-child(1)') do
diff --git a/spec/features/monitor_sidebar_link_spec.rb b/spec/features/monitor_sidebar_link_spec.rb
index fcef0fa0eff..3c59cd65cdb 100644
--- a/spec/features/monitor_sidebar_link_spec.rb
+++ b/spec/features/monitor_sidebar_link_spec.rb
@@ -45,7 +45,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
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
@@ -79,7 +78,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
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
@@ -98,7 +96,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
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
@@ -117,7 +114,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
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).to have_link('Serverless', href: project_serverless_functions_path(project))
expect(page).to have_link('Kubernetes', href: project_clusters_path(project))
end
@@ -134,7 +130,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
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
diff --git a/spec/features/oauth_login_spec.rb b/spec/features/oauth_login_spec.rb
index ea5bb8c33b2..fca8972b56c 100644
--- a/spec/features/oauth_login_spec.rb
+++ b/spec/features/oauth_login_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'OAuth Login', :js, :allow_forgery_protection do
+RSpec.describe 'OAuth Login', :allow_forgery_protection do
include DeviseHelpers
def enter_code(code)
@@ -28,7 +28,7 @@ RSpec.describe 'OAuth Login', :js, :allow_forgery_protection do
end
providers.each do |provider|
- context "when the user logs in using the #{provider} provider" do
+ context "when the user logs in using the #{provider} provider", :js do
let(:uid) { 'my-uid' }
let(:remember_me) { false }
let(:user) { create(:omniauth_user, extern_uid: uid, provider: provider.to_s) }
@@ -126,4 +126,55 @@ RSpec.describe 'OAuth Login', :js, :allow_forgery_protection do
end
end
end
+
+ context 'using GitLab as an OAuth provider' do
+ let_it_be(:user) { create(:user) }
+
+ let(:redirect_uri) { Gitlab::Routing.url_helpers.root_url }
+
+ # We can't use let_it_be to set the redirect_uri when creating the
+ # record as the host / port depends on whether or not the spec uses
+ # JS.
+ let(:application) do
+ create(:oauth_application, scopes: 'api', redirect_uri: redirect_uri, confidential: false)
+ end
+
+ let(:params) do
+ {
+ response_type: 'code',
+ client_id: application.uid,
+ redirect_uri: redirect_uri,
+ state: 'state'
+ }
+ end
+
+ before do
+ sign_in(user)
+
+ create(:oauth_access_token, application: application, resource_owner_id: user.id, scopes: 'api')
+ end
+
+ context 'when JS is enabled', :js do
+ it 'includes the fragment in the redirect if it is simple' do
+ visit "#{Gitlab::Routing.url_helpers.oauth_authorization_url(params)}#a_test-hash"
+
+ expect(page).to have_current_path("#{Gitlab::Routing.url_helpers.root_url}#a_test-hash", ignore_query: true)
+ end
+
+ it 'does not include the fragment if it contains forbidden characters' do
+ visit "#{Gitlab::Routing.url_helpers.oauth_authorization_url(params)}#a_test-hash."
+
+ expect(page).to have_current_path(Gitlab::Routing.url_helpers.root_url, ignore_query: true)
+ end
+ end
+
+ context 'when JS is disabled' do
+ it 'provides a basic HTML page including a link without the fragment' do
+ visit "#{Gitlab::Routing.url_helpers.oauth_authorization_url(params)}#a_test-hash"
+
+ expect(page).to have_current_path(oauth_authorization_path(params))
+ expect(page).to have_selector("a[href^='#{redirect_uri}']")
+ end
+ end
+ end
end
diff --git a/spec/features/profiles/keys_spec.rb b/spec/features/profiles/keys_spec.rb
index fde85a731a1..65944f5a537 100644
--- a/spec/features/profiles/keys_spec.rb
+++ b/spec/features/profiles/keys_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe 'Profile > SSH Keys' do
expect(page).to have_content("Title: #{attrs[:title]}")
expect(page).to have_content(attrs[:key])
- expect(find('.breadcrumbs-sub-title')).to have_link(attrs[:title])
+ expect(find('[data-testid="breadcrumb-current-link"]')).to have_link(attrs[:title])
end
it 'shows a confirmable warning if the key begins with an algorithm name that is unsupported' do
diff --git a/spec/features/profiles/oauth_applications_spec.rb b/spec/features/profiles/oauth_applications_spec.rb
index 6827dff5434..9d79041dc9d 100644
--- a/spec/features/profiles/oauth_applications_spec.rb
+++ b/spec/features/profiles/oauth_applications_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe 'Profile > Applications' do
visit oauth_application_path(application)
expect(page).to have_content("Application: #{application.name}")
- expect(find('.breadcrumbs-sub-title')).to have_link(application.name)
+ expect(find('[data-testid="breadcrumb-current-link"]')).to have_link(application.name)
end
it 'deletes an application' do
diff --git a/spec/features/projects/active_tabs_spec.rb b/spec/features/projects/active_tabs_spec.rb
index 2601dcf55c9..ff97d3c9503 100644
--- a/spec/features/projects/active_tabs_spec.rb
+++ b/spec/features/projects/active_tabs_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'Project active tab' do
- let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:project) { create(:project, :repository, :with_namespace_settings) }
let(:user) { project.first_owner }
diff --git a/spec/features/projects/blobs/blame_spec.rb b/spec/features/projects/blobs/blame_spec.rb
new file mode 100644
index 00000000000..bb3b5cd931c
--- /dev/null
+++ b/spec/features/projects/blobs/blame_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'File blame', :js do
+ include TreeHelper
+
+ let_it_be(:project) { create(:project, :public, :repository) }
+
+ let(:path) { 'CHANGELOG' }
+
+ def visit_blob_blame(path)
+ visit project_blame_path(project, tree_join('master', path))
+ wait_for_all_requests
+ end
+
+ it 'displays the blame page without pagination' do
+ visit_blob_blame(path)
+
+ expect(page).to have_css('.blame-commit')
+ expect(page).not_to have_css('.gl-pagination')
+ end
+
+ context 'when blob length is over the blame range limit' do
+ before do
+ stub_const('Projects::BlameService::PER_PAGE', 2)
+ end
+
+ it 'displays two first lines of the file with pagination' do
+ visit_blob_blame(path)
+
+ expect(page).to have_css('.blame-commit')
+ expect(page).to have_css('.gl-pagination')
+
+ expect(page).to have_css('#L1')
+ expect(page).not_to have_css('#L3')
+ expect(find('.page-link.active')).to have_text('1')
+ end
+
+ context 'when user clicks on the next button' do
+ before do
+ visit_blob_blame(path)
+
+ find('.js-next-button').click
+ end
+
+ it 'displays next two lines of the file with pagination' do
+ expect(page).not_to have_css('#L1')
+ expect(page).to have_css('#L3')
+ expect(find('.page-link.active')).to have_text('2')
+ end
+ end
+
+ context 'when feature flag disabled' do
+ before do
+ stub_feature_flags(blame_page_pagination: false)
+ end
+
+ it 'displays the blame page without pagination' do
+ visit_blob_blame(path)
+
+ expect(page).to have_css('.blame-commit')
+ expect(page).not_to have_css('.gl-pagination')
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/ci/editor_spec.rb b/spec/features/projects/ci/editor_spec.rb
index ad4381a526a..2f960c09936 100644
--- a/spec/features/projects/ci/editor_spec.rb
+++ b/spec/features/projects/ci/editor_spec.rb
@@ -12,6 +12,8 @@ RSpec.describe 'Pipeline Editor', :js do
let(:other_branch) { 'test' }
before do
+ stub_feature_flags(pipeline_editor_file_tree: false)
+
sign_in(user)
project.add_developer(user)
@@ -22,11 +24,7 @@ RSpec.describe 'Pipeline Editor', :js do
wait_for_requests
end
- it 'user sees the Pipeline Editor page' do
- expect(page).to have_content('Pipeline Editor')
- end
-
- describe 'Branch switcher' do
+ shared_examples 'default branch switcher behavior' do
def switch_to_branch(branch)
find('[data-testid="branch-selector"]').click
@@ -53,7 +51,7 @@ RSpec.describe 'Pipeline Editor', :js do
end
it 'displays new branch as selected after commiting on a new branch' do
- find('#target-branch-field').set('new_branch', clear: :backspace)
+ find('#source-branch-field').set('new_branch', clear: :backspace)
page.within('#source-editor-') do
find('textarea').send_keys '123'
@@ -68,6 +66,28 @@ RSpec.describe 'Pipeline Editor', :js do
end
end
+ it 'user sees the Pipeline Editor page' do
+ expect(page).to have_content('Pipeline Editor')
+ end
+
+ describe 'Branch Switcher (pipeline_editor_file_tree disabled)' do
+ it_behaves_like 'default branch switcher behavior'
+ end
+
+ describe 'Branch Switcher (pipeline_editor_file_tree enabled)' do
+ before do
+ stub_feature_flags(pipeline_editor_file_tree: true)
+
+ visit project_ci_pipeline_editor_path(project)
+ wait_for_requests
+
+ # close button for the popover
+ find('[data-testid="close-button"]').click
+ end
+
+ it_behaves_like 'default branch switcher behavior'
+ end
+
describe 'Editor navigation' do
context 'when no change is made' do
it 'user can navigate away without a browser alert' do
@@ -112,7 +132,7 @@ RSpec.describe 'Pipeline Editor', :js do
it 'user who creates a MR is taken to the merge request page without warnings' do
expect(page).not_to have_content('New merge request')
- find_field('Target Branch').set 'new_branch'
+ find_field('Branch').set 'new_branch'
find_field('Start a new merge request with these changes').click
click_button 'Commit changes'
diff --git a/spec/features/projects/ci/secure_files_spec.rb b/spec/features/projects/ci/secure_files_spec.rb
index 65c41eaf2ac..a0e9d663d35 100644
--- a/spec/features/projects/ci/secure_files_spec.rb
+++ b/spec/features/projects/ci/secure_files_spec.rb
@@ -7,13 +7,55 @@ RSpec.describe 'Secure Files', :js do
let(:user) { create(:user) }
before do
+ stub_feature_flags(ci_secure_files_read_only: false)
project.add_maintainer(user)
sign_in(user)
+ end
+ it 'user sees the Secure Files list component' do
visit project_ci_secure_files_path(project)
+ expect(page).to have_content('There are no records to show')
end
- it 'user sees the Secure Files list component' do
+ it 'prompts the user to confirm before deleting a file' do
+ file = create(:ci_secure_file, project: project)
+
+ visit project_ci_secure_files_path(project)
+
+ expect(page).to have_content(file.name)
+
+ find('button.btn-danger').click
+
+ expect(page).to have_content("Delete #{file.name}?")
+
+ click_on('Delete secure file')
+
+ visit project_ci_secure_files_path(project)
+
+ expect(page).not_to have_content(file.name)
+ end
+
+ it 'displays an uploaded file in the file list' do
+ visit project_ci_secure_files_path(project)
expect(page).to have_content('There are no records to show')
+
+ page.attach_file('spec/fixtures/ci_secure_files/upload-keystore.jks') do
+ click_button 'Upload File'
+ end
+
+ expect(page).to have_content('upload-keystore.jks')
+ end
+
+ it 'displays an error when a duplicate file upload is attempted' do
+ create(:ci_secure_file, project: project, name: 'upload-keystore.jks')
+ visit project_ci_secure_files_path(project)
+
+ expect(page).to have_content('upload-keystore.jks')
+
+ page.attach_file('spec/fixtures/ci_secure_files/upload-keystore.jks') do
+ click_button 'Upload File'
+ end
+
+ expect(page).to have_content('A file with this name already exists.')
end
end
diff --git a/spec/features/projects/clusters/eks_spec.rb b/spec/features/projects/clusters/eks_spec.rb
deleted file mode 100644
index 7e599ff1198..00000000000
--- a/spec/features/projects/clusters/eks_spec.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'AWS EKS Cluster', :js do
- let(:project) { create(:project) }
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- gitlab_sign_in(user)
- allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
- stub_application_setting(eks_integration_enabled: true)
- end
-
- context 'when user does not have a cluster and visits cluster index page' do
- let(:project_id) { 'test-project-1234' }
-
- before do
- visit project_clusters_path(project)
-
- click_button(class: 'dropdown-toggle-split')
- click_link 'Create a cluster (certificate - deprecated)'
- end
-
- context 'when user creates a cluster on AWS EKS' do
- before do
- click_link 'Amazon EKS'
- end
-
- it 'highlights Amazon EKS logo' do
- expect(page).to have_css('.js-create-aws-cluster-button.active')
- end
- end
- end
-end
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 491121a3743..a8a23ba1c85 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -14,11 +14,6 @@ RSpec.describe 'Gcp Cluster', :js do
allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
end
- def submit_form
- execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")')
- execute_script('document.querySelector(".js-gke-cluster-creation-submit").click()')
- end
-
context 'when user has signed with Google' do
let(:project_id) { 'test-project-1234' }
@@ -29,82 +24,7 @@ RSpec.describe 'Gcp Cluster', :js do
.to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
end
- context 'when user does not have a cluster and visits cluster index page' do
- before do
- visit project_clusters_path(project)
-
- visit_create_cluster_page
- click_link 'Google GKE'
- end
-
- it 'highlights Google GKE logo' do
- expect(page).to have_css('.js-create-gcp-cluster-button.active')
- end
-
- context 'when user filled form with valid parameters' do
- subject { submit_form }
-
- before do
- allow_any_instance_of(GoogleApi::CloudPlatform::Client)
- .to receive(:projects_zones_clusters_create) do
- double(
- 'cluster',
- self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123',
- status: 'RUNNING'
- )
- end
-
- allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil)
-
- expect(page).to have_css('.js-gcp-project-id-dropdown')
-
- execute_script('document.querySelector(".js-gcp-project-id-dropdown input").setAttribute("type", "text")')
- execute_script('document.querySelector(".js-gcp-zone-dropdown input").setAttribute("type", "text")')
- execute_script('document.querySelector(".js-gcp-machine-type-dropdown input").setAttribute("type", "text")')
-
- fill_in 'cluster[name]', with: 'dev-cluster'
- fill_in 'cluster[provider_gcp_attributes][gcp_project_id]', with: 'gcp-project-123'
- fill_in 'cluster[provider_gcp_attributes][zone]', with: 'us-central1-a'
- fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2'
- end
-
- it 'users sees a form with the GCP token' do
- expect(page).to have_selector(:css, 'form[data-token="token"]')
- end
-
- it 'user sees a cluster details page and creation status' do
- subject
-
- expect(page).to have_content('Kubernetes cluster is being created...')
-
- Clusters::Cluster.last.provider.make_created!
-
- expect(page).to have_content('Kubernetes cluster was successfully created')
- end
-
- it 'user sees a error if something wrong during creation' do
- subject
-
- expect(page).to have_content('Kubernetes cluster is being created...')
-
- Clusters::Cluster.last.provider.make_errored!('Something wrong!')
-
- expect(page).to have_content('Something wrong!')
- end
- end
-
- context 'when user filled form with invalid parameters' do
- before do
- submit_form
- end
-
- it 'user sees a validation error' do
- expect(page).to have_css('.gl-field-error')
- end
- end
- end
-
- context 'when user does have a cluster and visits cluster page' do
+ context 'when user have a cluster and visits cluster page' do
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
before do
@@ -167,12 +87,6 @@ RSpec.describe 'Gcp Cluster', :js do
it 'user sees offer on cluster index page' do
expect(page).to have_css('.gcp-signup-offer')
end
-
- it 'user sees offer on cluster create page' do
- visit_create_cluster_page
-
- expect(page).to have_css('.gcp-signup-offer')
- end
end
context 'when user has dismissed GCP signup offer' do
@@ -187,8 +101,6 @@ RSpec.describe 'Gcp Cluster', :js do
find('.gcp-signup-offer .js-close').click
wait_for_requests
- visit_create_cluster_page
-
expect(page).not_to have_css('.gcp-signup-offer')
end
end
@@ -216,9 +128,4 @@ RSpec.describe 'Gcp Cluster', :js do
expect(page).not_to have_css('.gcp-signup-offer')
end
end
-
- def visit_create_cluster_page
- click_button(class: 'dropdown-toggle-split')
- click_link 'Create a cluster (certificate - deprecated)'
- end
end
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index 0ecd7795964..9e1d66bc73e 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -81,101 +81,6 @@ RSpec.describe 'Clusters', :js do
end
end
end
-
- context 'when user adds a Google Kubernetes Engine cluster' do
- before do
- allow_any_instance_of(Projects::ClustersController)
- .to receive(:token_in_session).and_return('token')
- allow_any_instance_of(Projects::ClustersController)
- .to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
-
- allow_any_instance_of(Projects::ClustersController).to receive(:authorize_google_project_billing)
- allow_any_instance_of(Projects::ClustersController).to receive(:google_project_billing_status).and_return(true)
-
- allow_any_instance_of(GoogleApi::CloudPlatform::Client)
- .to receive(:projects_zones_clusters_create) do
- double(
- 'cluster_control',
- self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123',
- status: 'RUNNING'
- )
- end
-
- allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil)
-
- create(:cluster, :provided_by_gcp, name: 'default-cluster', environment_scope: '*', projects: [project])
- visit project_clusters_path(project)
- click_link 'Certificate'
- end
-
- context 'when user filled form with environment scope' do
- before do
- visit_create_cluster_page
- click_link 'Google GKE'
-
- sleep 2 # wait for ajax
- execute_script('document.querySelector(".js-gcp-project-id-dropdown input").setAttribute("type", "text")')
- execute_script('document.querySelector(".js-gcp-zone-dropdown input").setAttribute("type", "text")')
- execute_script('document.querySelector(".js-gcp-machine-type-dropdown input").setAttribute("type", "text")')
- execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")')
-
- fill_in 'cluster_name', with: 'staging-cluster'
- fill_in 'cluster_environment_scope', with: 'staging/*'
- fill_in 'cluster[provider_gcp_attributes][gcp_project_id]', with: 'gcp-project-123'
- fill_in 'cluster[provider_gcp_attributes][zone]', with: 'us-central1-a'
- fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2'
- click_button 'Create Kubernetes cluster'
-
- # The frontend won't show the details until the cluster is
- # created, and we don't want to make calls out to GCP.
- provider = Clusters::Cluster.last.provider
- provider.make_created
- end
-
- it 'user sees a cluster details page' do
- expect(page).to have_content('GitLab Integration')
- expect(page.find_field('cluster[environment_scope]').value).to eq('staging/*')
- end
- end
-
- context 'when user updates environment scope' do
- before do
- click_link 'default-cluster'
- fill_in 'cluster_environment_scope', with: 'production/*'
- within ".js-cluster-details-form" do
- click_button 'Save changes'
- end
- end
-
- it 'updates the environment scope' do
- expect(page.find_field('cluster[environment_scope]').value).to eq('production/*')
- end
- end
-
- context 'when user updates duplicated environment scope' do
- before do
- visit_create_cluster_page
- click_link 'Google GKE'
-
- sleep 2 # wait for ajax
- execute_script('document.querySelector(".js-gcp-project-id-dropdown input").setAttribute("type", "text")')
- execute_script('document.querySelector(".js-gcp-zone-dropdown input").setAttribute("type", "text")')
- execute_script('document.querySelector(".js-gcp-machine-type-dropdown input").setAttribute("type", "text")')
- execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")')
-
- fill_in 'cluster_name', with: 'staging-cluster'
- fill_in 'cluster_environment_scope', with: '*'
- fill_in 'cluster[provider_gcp_attributes][gcp_project_id]', with: 'gcp-project-123'
- fill_in 'cluster[provider_gcp_attributes][zone]', with: 'us-central1-a'
- fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2'
- click_button 'Create Kubernetes cluster'
- end
-
- it 'users sees an environment scope validation error' do
- expect(page).to have_content('cannot add duplicated environment scope')
- end
- end
- end
end
context 'when user has a cluster and visits cluster index page' do
@@ -221,7 +126,7 @@ RSpec.describe 'Clusters', :js do
visit project_clusters_path(project)
click_button(class: 'dropdown-toggle-split')
- click_link 'Create a cluster (certificate - deprecated)'
+ click_link 'Create a cluster'
end
def visit_connect_cluster_page
diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
index 57b35d81bb8..e472cff38ce 100644
--- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb
+++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do
it 'displays a mini pipeline graph' do
expect(page).to have_selector('[data-testid="commit-box-mini-graph"]')
- first('.mini-pipeline-graph-dropdown-toggle').click
+ first('[data-testid="mini-pipeline-graph-dropdown"]').click
wait_for_requests
diff --git a/spec/features/projects/graph_spec.rb b/spec/features/projects/graph_spec.rb
index 7e039a087c7..0b628ad1e9a 100644
--- a/spec/features/projects/graph_spec.rb
+++ b/spec/features/projects/graph_spec.rb
@@ -63,6 +63,23 @@ RSpec.describe 'Project Graph', :js do
end
end
+ context 'charts graph ref switcher' do
+ it 'switches ref to branch' do
+ ref_name = 'feature'
+ visit charts_project_graph_path(project, 'master')
+ first('.js-project-refs-dropdown').click
+
+ page.within '.project-refs-form' do
+ click_link ref_name
+ end
+
+ expect(page).to have_selector '.dropdown-menu-toggle', text: ref_name
+ page.within '.tree-ref-header' do
+ expect(page).to have_content ref_name
+ end
+ end
+ end
+
context 'when CI enabled' do
before do
project.enable_ci
diff --git a/spec/features/projects/integrations/prometheus_external_alerts_spec.rb b/spec/features/projects/integrations/prometheus_external_alerts_spec.rb
deleted file mode 100644
index 7e56ca13e23..00000000000
--- a/spec/features/projects/integrations/prometheus_external_alerts_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Prometheus external alerts', :js do
- include_context 'project integration activation'
-
- let(:alerts_section_selector) { '.js-prometheus-alerts' }
- let(:alerts_section) { page.find(alerts_section_selector) }
-
- context 'with manual configuration' do
- before do
- create(:prometheus_integration, project: project, api_url: 'http://prometheus.example.com', manual_configuration: '1', active: true)
- end
-
- it 'shows the Alerts section' do
- visit_project_integration('Prometheus')
-
- expect(alerts_section).to have_content('Alerts')
- expect(alerts_section).to have_content('Receive alerts from manually configured Prometheus servers.')
- expect(alerts_section).to have_content('URL')
- expect(alerts_section).to have_content('Authorization key')
- end
- end
-
- context 'with no configuration' do
- it 'does not show the Alerts section' do
- visit_project_integration('Prometheus')
- wait_for_requests
-
- expect(page).not_to have_css(alerts_section_selector)
- end
- end
-end
diff --git a/spec/features/projects/integrations/user_activates_prometheus_spec.rb b/spec/features/projects/integrations/user_activates_prometheus_spec.rb
index 80629af6fce..56b895919b8 100644
--- a/spec/features/projects/integrations/user_activates_prometheus_spec.rb
+++ b/spec/features/projects/integrations/user_activates_prometheus_spec.rb
@@ -9,14 +9,13 @@ RSpec.describe 'User activates Prometheus' do
stub_request(:get, /.*prometheus.example.com.*/)
end
- it 'does not activate integration and informs about deprecation', :js do
+ it 'saves and activates integration', :js do
visit_project_integration('Prometheus')
check('Active')
fill_in('API URL', with: 'http://prometheus.example.com')
click_button('Save changes')
- expect(page).not_to have_content('Prometheus settings saved and active.')
- expect(page).to have_content('Fields on this page have been deprecated.')
+ expect(page).to have_content('Prometheus settings saved and active.')
end
end
diff --git a/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb b/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb
index 48ae70d3ec9..27d0be23aec 100644
--- a/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb
+++ b/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb
@@ -10,9 +10,6 @@ RSpec.describe 'User uploads new design', :js do
let(:issue) { create(:issue, project: project) }
before do
- # Cause of raising query limiting threshold https://gitlab.com/gitlab-org/gitlab/-/issues/358845
- stub_const("Gitlab::QueryLimiting::Transaction::THRESHOLD", 106)
-
sign_in(user)
enable_design_management(feature_enabled)
visit project_issue_path(project, issue)
diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb
index 3b70d177fce..07b7a54974a 100644
--- a/spec/features/projects/jobs/user_browses_jobs_spec.rb
+++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe 'User browses jobs' do
stub_feature_flags(jobs_table_vue: false)
project.add_maintainer(user)
project.enable_ci
- project.update_attribute(:build_coverage_regex, /Coverage (\d+)%/)
+ build.update!(coverage_regex: '/Coverage (\d+)%/')
sign_in(user)
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index b34a615e651..befaf85fc1e 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -484,7 +484,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
end
context 'when job has an initial trace' do
- it 'loads job trace' do
+ it 'loads job logs' do
expect(page).to have_content 'BUILD TRACE'
job.trace.write(+'a+b') do |stream|
diff --git a/spec/features/projects/members/groups_with_access_list_spec.rb b/spec/features/projects/members/groups_with_access_list_spec.rb
index 9bd6476f836..821b9249aa8 100644
--- a/spec/features/projects/members/groups_with_access_list_spec.rb
+++ b/spec/features/projects/members/groups_with_access_list_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'Projects > Members > Groups with access list', :js do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, :public) }
- let_it_be(:project) { create(:project, :public) }
+ let_it_be(:project) { create(:project, :public, :with_namespace_settings) }
let_it_be(:expiration_date) { 5.days.from_now.to_date }
let(:additional_link_attrs) { {} }
diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/manage_groups_spec.rb
index a48229249e0..006fa3b6eff 100644
--- a/spec/features/projects/members/invite_group_spec.rb
+++ b/spec/features/projects/members/manage_groups_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project > Members > Invite group', :js do
+RSpec.describe 'Project > Members > Manage groups', :js do
include ActionView::Helpers::DateHelper
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::Features::InviteMembersModalHelper
@@ -126,7 +126,7 @@ RSpec.describe 'Project > Members > Invite group', :js do
end
describe 'setting an expiration date for a group link' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :with_namespace_settings) }
let!(:group) { create(:group) }
let_it_be(:expiration_date) { 5.days.from_now.to_date }
@@ -153,81 +153,18 @@ RSpec.describe 'Project > Members > Invite group', :js do
end
end
- describe 'the groups dropdown' do
+ describe 'group search results' do
let_it_be(:parent_group) { create(:group, :public) }
let_it_be(:project_group) { create(:group, :public, parent: parent_group) }
let_it_be(:project) { create(:project, group: project_group) }
- context 'with instance admin considerations' do
- let_it_be(:group_to_share) { create(:group) }
-
- context 'when user is an admin' do
- let_it_be(:admin) { create(:admin) }
-
- before do
- sign_in(admin)
- gitlab_enable_admin_mode_sign_in(admin)
- end
-
- it 'shows groups where the admin has no direct membership' do
- visit project_project_members_path(project)
-
- click_on 'Invite a group'
- click_on 'Select a group'
- wait_for_requests
-
- page.within(group_dropdown_selector) do
- expect_to_have_group(group_to_share)
- end
- end
-
- it 'shows groups where the admin has at least guest level membership' do
- group_to_share.add_guest(admin)
-
- visit project_project_members_path(project)
-
- click_on 'Invite a group'
- click_on 'Select a group'
- wait_for_requests
-
- page.within(group_dropdown_selector) do
- expect_to_have_group(group_to_share)
- end
- end
- end
-
- context 'when user is not an admin' do
- before do
- project.add_maintainer(maintainer)
- sign_in(maintainer)
- end
-
- it 'does not show groups where the user has no direct membership' do
- visit project_project_members_path(project)
-
- click_on 'Invite a group'
- click_on 'Select a group'
- wait_for_requests
-
- page.within(group_dropdown_selector) do
- expect_not_to_have_group(group_to_share)
- end
- end
-
- it 'shows groups where the user has at least guest level membership' do
- group_to_share.add_guest(maintainer)
-
- visit project_project_members_path(project)
-
- click_on 'Invite a group'
- click_on 'Select a group'
- wait_for_requests
-
- page.within(group_dropdown_selector) do
- expect_to_have_group(group_to_share)
- end
- end
- end
+ it_behaves_like 'inviting groups search results' do
+ let_it_be(:user) { maintainer }
+ let_it_be(:group) { parent_group }
+ let_it_be(:group_within_hierarchy) { create(:group, parent: group) }
+ let_it_be(:project_within_hierarchy) { create(:project, group: group_within_hierarchy)}
+ let_it_be(:members_page_path) { project_project_members_path(project) }
+ let_it_be(:members_page_path_within_hierarchy) { project_project_members_path(project_within_hierarchy) }
end
context 'for a project in a nested group' do
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 830ada29a2e..bd0874316ac 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
@@ -8,7 +8,7 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date
include Spec::Support::Helpers::Features::InviteMembersModalHelper
let_it_be(:maintainer) { create(:user) }
- let_it_be(:project) { create(:project) }
+ let_it_be(:project) { create(:project, :with_namespace_settings) }
let_it_be(:three_days_from_now) { 3.days.from_now.to_date }
let_it_be(:five_days_from_now) { 5.days.from_now.to_date }
diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb
index 4c3eaa93352..f4e8c55e3cc 100644
--- a/spec/features/projects/members/master_manages_access_requests_spec.rb
+++ b/spec/features/projects/members/master_manages_access_requests_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe 'Projects > Members > Maintainer manages access requests' do
it_behaves_like 'Maintainer manages access requests' do
- let(:entity) { create(:project, :public) }
+ let(:entity) { create(:project, :public, :with_namespace_settings) }
let(:members_page_path) { project_project_members_path(entity) }
end
end
diff --git a/spec/features/projects/members/member_leaves_project_spec.rb b/spec/features/projects/members/member_leaves_project_spec.rb
index 78a0a384d2c..67c40c1dbee 100644
--- a/spec/features/projects/members/member_leaves_project_spec.rb
+++ b/spec/features/projects/members/member_leaves_project_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe 'Projects > Members > Member leaves project' do
include Spec::Support::Helpers::Features::MembersHelpers
let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project, :repository, :with_namespace_settings) }
before do
project.add_developer(user)
diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb
index 5098908857a..023601b0b1e 100644
--- a/spec/features/projects/navbar_spec.rb
+++ b/spec/features/projects/navbar_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe 'Project navbar' do
stub_config(registry: { enabled: false })
stub_feature_flags(harbor_registry_integration: false)
- insert_package_nav(_('Infrastructure'))
+ insert_package_nav(_('Deployments'))
insert_infrastructure_registry_nav
insert_infrastructure_google_cloud_nav
end
@@ -49,7 +49,7 @@ RSpec.describe 'Project navbar' do
stub_config(pages: { enabled: true })
insert_after_sub_nav_item(
- _('Monitor'),
+ _('CI/CD'),
within: _('Settings'),
new_sub_nav_item_name: _('Pages')
)
@@ -67,7 +67,7 @@ RSpec.describe 'Project navbar' do
insert_container_nav
insert_after_sub_nav_item(
- _('Monitor'),
+ _('CI/CD'),
within: _('Settings'),
new_sub_nav_item_name: _('Packages & Registries')
)
diff --git a/spec/features/projects/packages_spec.rb b/spec/features/projects/packages_spec.rb
index 8180f6b9aff..f518cc1fc63 100644
--- a/spec/features/projects/packages_spec.rb
+++ b/spec/features/projects/packages_spec.rb
@@ -47,7 +47,8 @@ RSpec.describe 'Packages' do
let_it_be(:package) { create(:package, project: project) }
it 'allows you to delete a package' do
- first('[title="Remove package"]').click
+ find('[data-testid="delete-dropdown"]').click
+ find('[data-testid="action-delete"]').click
click_button('Delete package')
expect(page).to have_content 'Package deleted successfully'
diff --git a/spec/features/projects/pipelines/legacy_pipeline_spec.rb b/spec/features/projects/pipelines/legacy_pipeline_spec.rb
new file mode 100644
index 00000000000..a29cef393a8
--- /dev/null
+++ b/spec/features/projects/pipelines/legacy_pipeline_spec.rb
@@ -0,0 +1,1073 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Pipeline', :js do
+ include RoutesHelpers
+ include ProjectForksHelper
+ include ::ExclusiveLeaseHelpers
+
+ let_it_be(:project) { create(:project) }
+
+ let(:user) { create(:user) }
+ let(:role) { :developer }
+
+ before do
+ sign_in(user)
+ project.add_role(user, role)
+ stub_feature_flags(pipeline_tabs_vue: false)
+ end
+
+ shared_context 'pipeline builds' do
+ let!(:build_passed) do
+ create(:ci_build, :success,
+ pipeline: pipeline, stage: 'build', stage_idx: 0, name: 'build')
+ end
+
+ let!(:build_failed) do
+ create(:ci_build, :failed,
+ pipeline: pipeline, stage: 'test', stage_idx: 1, name: 'test')
+ end
+
+ let!(:build_preparing) do
+ create(:ci_build, :preparing,
+ pipeline: pipeline, stage: 'deploy', stage_idx: 2, name: 'prepare')
+ end
+
+ let!(:build_running) do
+ create(:ci_build, :running,
+ pipeline: pipeline, stage: 'deploy', stage_idx: 3, name: 'deploy')
+ end
+
+ let!(:build_manual) do
+ create(:ci_build, :manual,
+ pipeline: pipeline, stage: 'deploy', stage_idx: 3, name: 'manual-build')
+ end
+
+ let!(:build_scheduled) do
+ create(:ci_build, :scheduled,
+ pipeline: pipeline, stage: 'deploy', stage_idx: 3, name: 'delayed-job')
+ end
+
+ let!(:build_external) do
+ create(:generic_commit_status, status: 'success',
+ pipeline: pipeline,
+ name: 'jenkins',
+ stage: 'external',
+ ref: 'master',
+ target_url: 'http://gitlab.com/status')
+ end
+ end
+
+ describe 'GET /:project/-/pipelines/:id' do
+ include_context 'pipeline builds'
+
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project, reload: true) { create(:project, :repository, group: group) }
+
+ let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id, user: user) }
+
+ subject(:visit_pipeline) { visit project_pipeline_path(project, pipeline) }
+
+ it 'shows the pipeline graph' do
+ visit_pipeline
+
+ expect(page).to have_selector('.js-pipeline-graph')
+ expect(page).to have_content('Build')
+ expect(page).to have_content('Test')
+ expect(page).to have_content('Deploy')
+ expect(page).to have_content('Retry')
+ expect(page).to have_content('Cancel running')
+ end
+
+ it 'shows Pipeline tab pane as active' do
+ visit_pipeline
+
+ expect(page).to have_css('#js-tab-pipeline.active')
+ end
+
+ it 'shows link to the pipeline ref' do
+ visit_pipeline
+
+ expect(page).to have_link(pipeline.ref)
+ end
+
+ it 'shows the pipeline information' do
+ visit_pipeline
+
+ within '.pipeline-info' do
+ expect(page).to have_content("#{pipeline.statuses.count} jobs " \
+ "for #{pipeline.ref}")
+ expect(page).to have_link(pipeline.ref,
+ href: project_commits_path(pipeline.project, pipeline.ref))
+ end
+ end
+
+ describe 'related merge requests' do
+ context 'when there are no related merge requests' do
+ it 'shows a "no related merge requests" message' do
+ visit_pipeline
+
+ within '.related-merge-request-info' do
+ expect(page).to have_content('No related merge requests found.')
+ end
+ end
+ end
+
+ context 'when there is one related merge request' do
+ let!(:merge_request) do
+ create(:merge_request,
+ source_project: project,
+ source_branch: pipeline.ref)
+ end
+
+ it 'shows a link to the merge request' do
+ visit_pipeline
+
+ within '.related-merge-requests' do
+ expect(page).to have_content('1 related merge request: ')
+ expect(page).to have_selector('.js-truncated-mr-list')
+ expect(page).to have_link("#{merge_request.to_reference} #{merge_request.title}")
+
+ expect(page).not_to have_selector('.js-full-mr-list')
+ expect(page).not_to have_selector('.text-expander')
+ end
+ end
+ end
+
+ context 'when there are two related merge requests' do
+ let!(:merge_request1) do
+ create(:merge_request,
+ source_project: project,
+ source_branch: pipeline.ref)
+ end
+
+ let!(:merge_request2) do
+ create(:merge_request,
+ source_project: project,
+ source_branch: pipeline.ref,
+ target_branch: 'fix')
+ end
+
+ it 'links to the most recent related merge request' do
+ visit_pipeline
+
+ within '.related-merge-requests' do
+ expect(page).to have_content('2 related merge requests: ')
+ expect(page).to have_link("#{merge_request2.to_reference} #{merge_request2.title}")
+ expect(page).to have_selector('.text-expander')
+ expect(page).to have_selector('.js-full-mr-list', visible: false)
+ end
+ end
+
+ it 'expands to show links to all related merge requests' do
+ visit_pipeline
+
+ within '.related-merge-requests' do
+ find('.text-expander').click
+
+ expect(page).to have_selector('.js-full-mr-list', visible: true)
+
+ pipeline.all_merge_requests.map do |merge_request|
+ expect(page).to have_link(href: project_merge_request_path(project, merge_request))
+ end
+ end
+ end
+ end
+ end
+
+ describe 'pipelines details view' do
+ let!(:status) { create(:user_status, user: pipeline.user, emoji: 'smirk', message: 'Authoring this object') }
+
+ it 'pipeline header shows the user status and emoji' do
+ visit project_pipeline_path(project, pipeline)
+
+ within '[data-testid="ci-header-content"]' do
+ expect(page).to have_selector("[data-testid='#{status.message}']")
+ expect(page).to have_selector("[data-name='#{status.emoji}']")
+ end
+ end
+ end
+
+ describe 'pipeline graph' do
+ before do
+ visit_pipeline
+ end
+
+ context 'when pipeline has running builds' do
+ it 'shows a running icon and a cancel action for the running build' do
+ page.within('#ci-badge-deploy') do
+ expect(page).to have_selector('.js-ci-status-icon-running')
+ expect(page).to have_selector('.js-icon-cancel')
+ expect(page).to have_content('deploy')
+ end
+ end
+
+ it 'cancels the running build and shows retry button', :sidekiq_might_not_need_inline do
+ find('#ci-badge-deploy .ci-action-icon-container').click
+
+ page.within('#ci-badge-deploy') do
+ expect(page).to have_css('.js-icon-retry')
+ end
+ end
+ end
+
+ context 'when pipeline has preparing builds' do
+ it 'shows a preparing icon and a cancel action' do
+ page.within('#ci-badge-prepare') do
+ expect(page).to have_selector('.js-ci-status-icon-preparing')
+ expect(page).to have_selector('.js-icon-cancel')
+ expect(page).to have_content('prepare')
+ end
+ end
+
+ it 'cancels the preparing build and shows retry button', :sidekiq_might_not_need_inline do
+ find('#ci-badge-deploy .ci-action-icon-container').click
+
+ page.within('#ci-badge-deploy') do
+ expect(page).to have_css('.js-icon-retry')
+ end
+ end
+ end
+
+ context 'when pipeline has successful builds' do
+ it 'shows the success icon and a retry action for the successful build' do
+ page.within('#ci-badge-build') do
+ expect(page).to have_selector('.js-ci-status-icon-success')
+ expect(page).to have_content('build')
+ end
+
+ page.within('#ci-badge-build .ci-action-icon-container.js-icon-retry') do
+ expect(page).to have_selector('svg')
+ end
+ end
+
+ it 'is possible to retry the success job', :sidekiq_might_not_need_inline do
+ find('#ci-badge-build .ci-action-icon-container').click
+ wait_for_requests
+
+ expect(page).not_to have_content('Retry job')
+ within('.js-pipeline-header-container') do
+ expect(page).to have_selector('.js-ci-status-icon-running')
+ end
+ end
+ end
+
+ context 'when pipeline has a delayed job' do
+ let(:project) { create(:project, :repository, group: group) }
+
+ it 'shows the scheduled icon and an unschedule action for the delayed job' do
+ page.within('#ci-badge-delayed-job') do
+ expect(page).to have_selector('.js-ci-status-icon-scheduled')
+ expect(page).to have_content('delayed-job')
+ end
+
+ page.within('#ci-badge-delayed-job .ci-action-icon-container.js-icon-time-out') do
+ expect(page).to have_selector('svg')
+ end
+ end
+
+ it 'unschedules the delayed job and shows play button as a manual job', :sidekiq_might_not_need_inline do
+ find('#ci-badge-delayed-job .ci-action-icon-container').click
+
+ page.within('#ci-badge-delayed-job') do
+ expect(page).to have_css('.js-icon-play')
+ end
+ end
+ end
+
+ context 'when pipeline has failed builds' do
+ it 'shows the failed icon and a retry action for the failed build' do
+ page.within('#ci-badge-test') do
+ expect(page).to have_selector('.js-ci-status-icon-failed')
+ expect(page).to have_content('test')
+ end
+
+ page.within('#ci-badge-test .ci-action-icon-container.js-icon-retry') do
+ expect(page).to have_selector('svg')
+ end
+ end
+
+ it 'is possible to retry the failed build', :sidekiq_might_not_need_inline do
+ find('#ci-badge-test .ci-action-icon-container').click
+ wait_for_requests
+
+ expect(page).not_to have_content('Retry job')
+ within('.js-pipeline-header-container') do
+ expect(page).to have_selector('.js-ci-status-icon-running')
+ end
+ end
+
+ it 'includes the failure reason' do
+ page.within('#ci-badge-test') do
+ build_link = page.find('.js-pipeline-graph-job-link')
+ expect(build_link['title']).to eq('test - failed - (unknown failure)')
+ end
+ end
+ end
+
+ context 'when pipeline has manual jobs' do
+ it 'shows the skipped icon and a play action for the manual build' do
+ page.within('#ci-badge-manual-build') do
+ expect(page).to have_selector('.js-ci-status-icon-manual')
+ expect(page).to have_content('manual')
+ end
+
+ page.within('#ci-badge-manual-build .ci-action-icon-container.js-icon-play') do
+ expect(page).to have_selector('svg')
+ end
+ end
+
+ it 'is possible to play the manual job', :sidekiq_might_not_need_inline do
+ find('#ci-badge-manual-build .ci-action-icon-container').click
+ wait_for_requests
+
+ expect(page).not_to have_content('Play job')
+ within('.js-pipeline-header-container') do
+ expect(page).to have_selector('.js-ci-status-icon-running')
+ end
+ end
+ end
+
+ context 'when pipeline has external job' do
+ it 'shows the success icon and the generic comit status build' do
+ expect(page).to have_selector('.js-ci-status-icon-success')
+ expect(page).to have_content('jenkins')
+ expect(page).to have_link('jenkins', href: 'http://gitlab.com/status')
+ end
+ end
+ end
+
+ context 'when the pipeline has manual stage' do
+ before do
+ create(:ci_build, :manual, pipeline: pipeline, stage_idx: 10, stage: 'publish', name: 'CentOS')
+ create(:ci_build, :manual, pipeline: pipeline, stage_idx: 10, stage: 'publish', name: 'Debian')
+ create(:ci_build, :manual, pipeline: pipeline, stage_idx: 10, stage: 'publish', name: 'OpenSUDE')
+
+ # force to update stages statuses
+ Ci::ProcessPipelineService.new(pipeline).execute
+
+ visit_pipeline
+ end
+
+ it 'displays play all button' do
+ expect(page).to have_selector('.js-stage-action')
+ end
+ end
+
+ context 'page tabs' do
+ before do
+ visit_pipeline
+ end
+
+ it 'shows Pipeline, Jobs, DAG and Failed Jobs tabs with link' do
+ expect(page).to have_link('Pipeline')
+ expect(page).to have_link('Jobs')
+ expect(page).to have_link('Needs')
+ expect(page).to have_link('Failed Jobs')
+ end
+
+ it 'shows counter in Jobs tab' do
+ expect(page.find('.js-builds-counter').text).to eq(pipeline.total_size.to_s)
+ end
+
+ it 'shows Pipeline tab as active' do
+ expect(page).to have_css('.js-pipeline-tab-link .active')
+ end
+
+ context 'without permission to access builds' do
+ let(:project) { create(:project, :public, :repository, public_builds: false) }
+ let(:role) { :guest }
+
+ it 'does not show the pipeline details page' do
+ expect(page).to have_content('Not Found')
+ end
+ end
+ end
+
+ context 'retrying jobs' do
+ before do
+ visit_pipeline
+ end
+
+ it { expect(page).not_to have_content('retried') }
+
+ context 'when retrying' do
+ before do
+ find('[data-testid="retryPipeline"]').click
+ wait_for_requests
+ end
+
+ it 'does not show a "Retry" button', :sidekiq_might_not_need_inline do
+ expect(page).not_to have_content('Retry')
+ end
+
+ it 'shows running status in pipeline header', :sidekiq_might_not_need_inline do
+ within('.js-pipeline-header-container') do
+ expect(page).to have_selector('.js-ci-status-icon-running')
+ end
+ end
+ end
+ end
+
+ context 'canceling jobs' do
+ before do
+ visit_pipeline
+ end
+
+ it { expect(page).not_to have_selector('.ci-canceled') }
+
+ context 'when canceling' do
+ before do
+ click_on 'Cancel running'
+ end
+
+ it 'does not show a "Cancel running" button', :sidekiq_might_not_need_inline do
+ expect(page).not_to have_content('Cancel running')
+ end
+ end
+ end
+
+ context 'when user can not delete' do
+ before do
+ visit_pipeline
+ end
+
+ it { expect(page).not_to have_button('Delete') }
+ end
+
+ context 'when deleting' do
+ before do
+ group.add_owner(user)
+
+ visit_pipeline
+
+ click_button 'Delete'
+ click_button 'Delete pipeline'
+ end
+
+ it 'redirects to pipeline overview page', :sidekiq_inline do
+ expect(page).to have_content('The pipeline has been deleted')
+ expect(page).to have_current_path(project_pipelines_path(project), ignore_query: true)
+ end
+ end
+
+ context 'when pipeline ref does not exist in repository anymore' do
+ let(:pipeline) do
+ create(:ci_empty_pipeline, project: project,
+ ref: 'non-existent',
+ sha: project.commit.id,
+ user: user)
+ end
+
+ before do
+ visit_pipeline
+ end
+
+ it 'does not render link to the pipeline ref' do
+ expect(page).not_to have_link(pipeline.ref)
+ expect(page).to have_content(pipeline.ref)
+ end
+
+ it 'does not render render raw HTML to the pipeline ref' do
+ page.within '.pipeline-info' do
+ expect(page).not_to have_content('<span class="ref-name"')
+ end
+ end
+ end
+
+ context 'when pipeline is detached merge request pipeline' do
+ let(:source_project) { project }
+ let(:target_project) { project }
+
+ let(:merge_request) do
+ create(:merge_request,
+ :with_detached_merge_request_pipeline,
+ source_project: source_project,
+ target_project: target_project)
+ end
+
+ let(:pipeline) do
+ merge_request.all_pipelines.last
+ end
+
+ it 'shows the pipeline information' do
+ visit_pipeline
+
+ within '.pipeline-info' do
+ expect(page).to have_content("#{pipeline.statuses.count} jobs " \
+ "for !#{merge_request.iid} " \
+ "with #{merge_request.source_branch}")
+ expect(page).to have_link("!#{merge_request.iid}",
+ href: project_merge_request_path(project, merge_request))
+ expect(page).to have_link(merge_request.source_branch,
+ href: project_commits_path(merge_request.source_project, merge_request.source_branch))
+ end
+ end
+
+ context 'when source branch does not exist' do
+ before do
+ project.repository.rm_branch(user, merge_request.source_branch)
+ end
+
+ it 'does not link to the source branch commit path' do
+ visit_pipeline
+
+ within '.pipeline-info' do
+ expect(page).not_to have_link(merge_request.source_branch)
+ expect(page).to have_content(merge_request.source_branch)
+ end
+ end
+ end
+
+ context 'when source project is a forked project' do
+ let(:source_project) { fork_project(project, user, repository: true) }
+
+ before do
+ visit project_pipeline_path(source_project, pipeline)
+ end
+
+ it 'shows the pipeline information', :sidekiq_might_not_need_inline do
+ within '.pipeline-info' do
+ expect(page).to have_content("#{pipeline.statuses.count} jobs " \
+ "for !#{merge_request.iid} " \
+ "with #{merge_request.source_branch}")
+ expect(page).to have_link("!#{merge_request.iid}",
+ href: project_merge_request_path(project, merge_request))
+ expect(page).to have_link(merge_request.source_branch,
+ href: project_commits_path(merge_request.source_project, merge_request.source_branch))
+ end
+ end
+ end
+ end
+
+ context 'when pipeline is merge request pipeline' do
+ let(:project) { create(:project, :repository, group: group) }
+ let(:source_project) { project }
+ let(:target_project) { project }
+
+ let(:merge_request) do
+ create(:merge_request,
+ :with_merge_request_pipeline,
+ source_project: source_project,
+ target_project: target_project,
+ merge_sha: project.commit.id)
+ end
+
+ let(:pipeline) do
+ merge_request.all_pipelines.last
+ end
+
+ before do
+ pipeline.update!(user: user)
+ end
+
+ it 'shows the pipeline information' do
+ visit_pipeline
+
+ within '.pipeline-info' do
+ expect(page).to have_content("#{pipeline.statuses.count} jobs " \
+ "for !#{merge_request.iid} " \
+ "with #{merge_request.source_branch} " \
+ "into #{merge_request.target_branch}")
+ expect(page).to have_link("!#{merge_request.iid}",
+ href: project_merge_request_path(project, merge_request))
+ expect(page).to have_link(merge_request.source_branch,
+ href: project_commits_path(merge_request.source_project, merge_request.source_branch))
+ expect(page).to have_link(merge_request.target_branch,
+ href: project_commits_path(merge_request.target_project, merge_request.target_branch))
+ end
+ end
+
+ context 'when target branch does not exist' do
+ before do
+ project.repository.rm_branch(user, merge_request.target_branch)
+ end
+
+ it 'does not link to the target branch commit path' do
+ visit_pipeline
+
+ within '.pipeline-info' do
+ expect(page).not_to have_link(merge_request.target_branch)
+ expect(page).to have_content(merge_request.target_branch)
+ end
+ end
+ end
+
+ context 'when source project is a forked project' do
+ let(:source_project) { fork_project(project, user, repository: true) }
+
+ before do
+ visit project_pipeline_path(source_project, pipeline)
+ end
+
+ it 'shows the pipeline information', :sidekiq_might_not_need_inline do
+ within '.pipeline-info' do
+ expect(page).to have_content("#{pipeline.statuses.count} jobs " \
+ "for !#{merge_request.iid} " \
+ "with #{merge_request.source_branch} " \
+ "into #{merge_request.target_branch}")
+ expect(page).to have_link("!#{merge_request.iid}",
+ href: project_merge_request_path(project, merge_request))
+ expect(page).to have_link(merge_request.source_branch,
+ href: project_commits_path(merge_request.source_project, merge_request.source_branch))
+ expect(page).to have_link(merge_request.target_branch,
+ href: project_commits_path(merge_request.target_project, merge_request.target_branch))
+ end
+ end
+ end
+ end
+ end
+
+ context 'when user does not have access to read jobs' do
+ before do
+ project.update!(public_builds: false)
+ end
+
+ describe 'GET /:project/-/pipelines/:id' do
+ include_context 'pipeline builds'
+
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id, user: user) }
+
+ before do
+ visit project_pipeline_path(project, pipeline)
+ end
+
+ it 'shows the pipeline graph' do
+ expect(page).to have_selector('.js-pipeline-graph')
+ expect(page).to have_content('Build')
+ expect(page).to have_content('Test')
+ expect(page).to have_content('Deploy')
+ expect(page).to have_content('Retry')
+ expect(page).to have_content('Cancel running')
+ end
+
+ it 'does not link to job' do
+ expect(page).not_to have_selector('.js-pipeline-graph-job-link')
+ end
+ end
+ end
+
+ context 'when a bridge job exists' do
+ include_context 'pipeline builds'
+
+ let(:project) { create(:project, :repository) }
+ let(:downstream) { create(:project, :repository) }
+
+ let(:pipeline) do
+ create(:ci_pipeline, project: project,
+ ref: 'master',
+ sha: project.commit.id,
+ user: user)
+ end
+
+ let!(:bridge) do
+ create(:ci_bridge, pipeline: pipeline,
+ name: 'cross-build',
+ user: user,
+ downstream: downstream)
+ end
+
+ describe 'GET /:project/-/pipelines/:id' do
+ before do
+ visit project_pipeline_path(project, pipeline)
+ end
+
+ it 'shows the pipeline with a bridge job' do
+ expect(page).to have_selector('.js-pipeline-graph')
+ expect(page).to have_content('cross-build')
+ end
+
+ context 'when a scheduled pipeline is created by a blocked user' do
+ let(:project) { create(:project, :repository) }
+
+ let(:schedule) do
+ create(:ci_pipeline_schedule,
+ project: project,
+ owner: project.first_owner,
+ description: 'blocked user schedule'
+ ).tap do |schedule|
+ schedule.update_column(:next_run_at, 1.minute.ago)
+ end
+ end
+
+ before do
+ schedule.owner.block!
+
+ begin
+ PipelineScheduleWorker.new.perform
+ rescue Ci::CreatePipelineService::CreateError
+ # Do nothing, assert view code after the Pipeline failed to create.
+ end
+ end
+
+ it 'displays the PipelineSchedule in an inactive state' do
+ visit project_pipeline_schedules_path(project)
+ page.click_link('Inactive')
+
+ expect(page).to have_selector('table.ci-table > tbody > tr > td', text: 'blocked user schedule')
+ end
+
+ it 'does not create a new Pipeline' do
+ visit project_pipelines_path(project)
+
+ expect(page).not_to have_selector('.ci-table')
+ expect(schedule.last_pipeline).to be_nil
+ end
+ end
+ end
+
+ describe 'GET /:project/-/pipelines/:id/builds' do
+ before do
+ visit builds_project_pipeline_path(project, pipeline)
+ end
+
+ it 'shows a bridge job on a list' do
+ expect(page).to have_content('cross-build')
+ expect(page).to have_content(bridge.id)
+ end
+ end
+ end
+
+ 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) }
+
+ let!(:test_job) do
+ create(:ci_build, :pending, stage: 'test', name: 'test',
+ stage_idx: 1, pipeline: pipeline, project: project)
+ end
+
+ let!(:deploy_job) do
+ create(:ci_build, :created, stage: 'deploy', name: 'deploy',
+ stage_idx: 2, pipeline: pipeline, project: project, resource_group: resource_group)
+ end
+
+ describe 'GET /:project/-/pipelines/:id' do
+ subject { visit project_pipeline_path(project, pipeline) }
+
+ it 'shows deploy job as created' do
+ subject
+
+ within('.js-pipeline-header-container') do
+ expect(page).to have_content('pending')
+ end
+
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[0]) do
+ expect(page).to have_content('test')
+ expect(page).to have_css('.ci-status-icon-pending')
+ end
+
+ within(all('[data-testid="stage-column"]')[1]) do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-created')
+ end
+ end
+ end
+
+ context 'when test job succeeded' do
+ before do
+ test_job.success!
+ end
+
+ it 'shows deploy job as pending' do
+ subject
+
+ within('.js-pipeline-header-container') do
+ expect(page).to have_content('running')
+ end
+
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[0]) do
+ expect(page).to have_content('test')
+ expect(page).to have_css('.ci-status-icon-success')
+ end
+
+ within(all('[data-testid="stage-column"]')[1]) do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-pending')
+ end
+ end
+ end
+ end
+
+ context 'when test job succeeded but there are no available resources' do
+ let(:another_job) { create(:ci_build, :running, project: project, resource_group: resource_group) }
+
+ before do
+ resource_group.assign_resource_to(another_job)
+ test_job.success!
+ end
+
+ it 'shows deploy job as waiting for resource' do
+ subject
+
+ within('.js-pipeline-header-container') do
+ expect(page).to have_content('waiting')
+ end
+
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-waiting-for-resource')
+ end
+ end
+ end
+
+ context 'when resource is released from another job' do
+ before do
+ another_job.success!
+ end
+
+ it 'shows deploy job as pending' do
+ subject
+
+ within('.js-pipeline-header-container') do
+ expect(page).to have_content('running')
+ end
+
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-pending')
+ end
+ end
+ end
+ end
+
+ context 'when deploy job is a bridge to trigger a downstream pipeline' do
+ let!(:deploy_job) do
+ create(:ci_bridge, :created, stage: 'deploy', name: 'deploy',
+ stage_idx: 2, pipeline: pipeline, project: project, resource_group: resource_group)
+ end
+
+ it 'shows deploy job as waiting for resource' do
+ subject
+
+ within('.js-pipeline-header-container') do
+ expect(page).to have_content('waiting')
+ end
+
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-waiting-for-resource')
+ end
+ end
+ end
+ end
+
+ context 'when deploy job is a bridge to trigger a downstream pipeline' do
+ let!(:deploy_job) do
+ create(:ci_bridge, :created, stage: 'deploy', name: 'deploy',
+ stage_idx: 2, pipeline: pipeline, project: project, resource_group: resource_group)
+ end
+
+ it 'shows deploy job as waiting for resource' do
+ subject
+
+ within('.js-pipeline-header-container') do
+ expect(page).to have_content('waiting')
+ end
+
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-waiting-for-resource')
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ describe 'GET /:project/-/pipelines/:id/dag' do
+ include_context 'pipeline builds'
+
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
+
+ before do
+ visit dag_project_pipeline_path(project, pipeline)
+ end
+
+ it 'shows DAG tab pane as active' do
+ expect(page).to have_css('#js-tab-dag.active', visible: false)
+ end
+
+ context 'page tabs' do
+ it 'shows Pipeline, Jobs and DAG tabs with link' do
+ expect(page).to have_link('Pipeline')
+ expect(page).to have_link('Jobs')
+ expect(page).to have_link('DAG')
+ end
+
+ it 'shows counter in Jobs tab' do
+ expect(page.find('.js-builds-counter').text).to eq(pipeline.total_size.to_s)
+ end
+
+ it 'shows DAG tab as active' do
+ expect(page).to have_css('li.js-dag-tab-link .active')
+ end
+ end
+ end
+
+ context 'when user sees pipeline flags in a pipeline detail page' do
+ let_it_be(:project) { create(:project, :repository) }
+
+ context 'when pipeline is latest' do
+ include_context 'pipeline builds'
+
+ let(:pipeline) do
+ create(:ci_pipeline,
+ project: project,
+ ref: 'master',
+ sha: project.commit.id,
+ user: user)
+ end
+
+ before do
+ visit project_pipeline_path(project, pipeline)
+ end
+
+ it 'contains badge that indicates it is the latest build' do
+ page.within(all('.well-segment')[1]) do
+ expect(page).to have_content 'latest'
+ end
+ end
+ end
+
+ context 'when pipeline has configuration errors' do
+ let(:pipeline) do
+ create(:ci_pipeline,
+ :invalid,
+ project: project,
+ ref: 'master',
+ sha: project.commit.id,
+ user: user)
+ end
+
+ before do
+ visit project_pipeline_path(project, pipeline)
+ end
+
+ it 'contains badge that indicates errors' do
+ page.within(all('.well-segment')[1]) do
+ expect(page).to have_content 'yaml invalid'
+ end
+ end
+
+ it 'contains badge with tooltip which contains error' do
+ expect(pipeline).to have_yaml_errors
+
+ page.within(all('.well-segment')[1]) do
+ expect(page).to have_selector(
+ %Q{span[title="#{pipeline.yaml_errors}"]})
+ end
+ end
+
+ it 'contains badge that indicates failure reason' do
+ expect(page).to have_content 'error'
+ end
+
+ it 'contains badge with tooltip which contains failure reason' do
+ expect(pipeline.failure_reason?).to eq true
+
+ page.within(all('.well-segment')[1]) do
+ expect(page).to have_selector(
+ %Q{span[title="#{pipeline.present.failure_reason}"]})
+ end
+ end
+
+ it 'contains a pipeline header with title' do
+ expect(page).to have_content "Pipeline ##{pipeline.id}"
+ end
+ end
+
+ context 'when pipeline is stuck' do
+ include_context 'pipeline builds'
+
+ let(:pipeline) do
+ create(:ci_pipeline,
+ project: project,
+ ref: 'master',
+ sha: project.commit.id,
+ user: user)
+ end
+
+ before do
+ create(:ci_build, :pending, pipeline: pipeline)
+ visit project_pipeline_path(project, pipeline)
+ end
+
+ it 'contains badge that indicates being stuck' do
+ page.within(all('.well-segment')[1]) do
+ expect(page).to have_content 'stuck'
+ end
+ end
+ end
+
+ context 'when pipeline uses auto devops' do
+ include_context 'pipeline builds'
+
+ let(:project) { create(:project, :repository, auto_devops_attributes: { enabled: true }) }
+ let(:pipeline) do
+ create(:ci_pipeline,
+ :auto_devops_source,
+ project: project,
+ ref: 'master',
+ sha: project.commit.id,
+ user: user)
+ end
+
+ before do
+ visit project_pipeline_path(project, pipeline)
+ end
+
+ it 'contains badge that indicates using auto devops' do
+ page.within(all('.well-segment')[1]) do
+ expect(page).to have_content 'Auto DevOps'
+ end
+ end
+ end
+
+ context 'when pipeline runs in a merge request context' do
+ include_context 'pipeline builds'
+
+ let(:pipeline) do
+ create(:ci_pipeline,
+ source: :merge_request_event,
+ project: merge_request.source_project,
+ ref: 'feature',
+ sha: merge_request.diff_head_sha,
+ user: user,
+ merge_request: merge_request)
+ end
+
+ let(:merge_request) do
+ create(:merge_request,
+ source_project: project,
+ source_branch: 'feature',
+ target_project: project,
+ target_branch: 'master')
+ end
+
+ before do
+ visit project_pipeline_path(project, pipeline)
+ end
+
+ it 'contains badge that indicates detached merge request pipeline' do
+ page.within(all('.well-segment')[1]) do
+ expect(page).to have_content 'merge request'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/pipelines/legacy_pipelines_spec.rb b/spec/features/projects/pipelines/legacy_pipelines_spec.rb
new file mode 100644
index 00000000000..3f89e344c51
--- /dev/null
+++ b/spec/features/projects/pipelines/legacy_pipelines_spec.rb
@@ -0,0 +1,847 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Pipelines', :js do
+ include ProjectForksHelper
+ include Spec::Support::Helpers::ModalHelpers
+
+ let(:project) { create(:project) }
+ let(:expected_detached_mr_tag) {'merge request'}
+
+ context 'when user is logged in' do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+
+ project.add_developer(user)
+ project.update!(auto_devops_attributes: { enabled: false })
+
+ stub_feature_flags(pipeline_tabs_vue: false)
+ end
+
+ describe 'GET /:project/-/pipelines' do
+ let(:project) { create(:project, :repository) }
+
+ let!(:pipeline) do
+ create(
+ :ci_empty_pipeline,
+ project: project,
+ ref: 'master',
+ status: 'running',
+ sha: project.commit.id
+ )
+ end
+
+ context 'scope' do
+ before do
+ create(:ci_empty_pipeline, status: 'pending', project: project, sha: project.commit.id, ref: 'master')
+ create(:ci_empty_pipeline, status: 'running', project: project, sha: project.commit.id, ref: 'master')
+ create(:ci_empty_pipeline, status: 'created', project: project, sha: project.commit.id, ref: 'master')
+ create(:ci_empty_pipeline, status: 'success', project: project, sha: project.commit.id, ref: 'master')
+ end
+
+ [:all, :running, :pending, :finished, :branches].each do |scope|
+ context "when displaying #{scope}" do
+ before do
+ visit_project_pipelines(scope: scope)
+ end
+
+ it 'contains pipeline commit short SHA' do
+ expect(page).to have_content(pipeline.short_sha)
+ end
+
+ it 'contains branch name' do
+ expect(page).to have_content(pipeline.ref)
+ end
+ end
+ end
+ end
+
+ context 'header tabs' do
+ before do
+ visit project_pipelines_path(project)
+ wait_for_requests
+ end
+
+ it 'shows a tab for All pipelines and count' do
+ expect(page.find('.js-pipelines-tab-all').text).to include('All')
+ expect(page.find('.js-pipelines-tab-all .badge').text).to include('1')
+ end
+
+ it 'shows a tab for Finished pipelines and count' do
+ expect(page.find('.js-pipelines-tab-finished').text).to include('Finished')
+ end
+
+ it 'shows a tab for Branches' do
+ expect(page.find('.js-pipelines-tab-branches').text).to include('Branches')
+ end
+
+ it 'shows a tab for Tags' do
+ expect(page.find('.js-pipelines-tab-tags').text).to include('Tags')
+ end
+
+ it 'updates content when tab is clicked' do
+ page.find('.js-pipelines-tab-finished').click
+ wait_for_requests
+ expect(page).to have_content('There are currently no finished pipelines.')
+ end
+ end
+
+ context 'navigation links' do
+ before do
+ visit project_pipelines_path(project)
+ wait_for_requests
+ end
+
+ it 'renders "CI lint" link' do
+ expect(page).to have_link('CI lint')
+ end
+
+ it 'renders "Run pipeline" link' do
+ expect(page).to have_link('Run pipeline')
+ end
+ end
+
+ context 'when pipeline is cancelable' do
+ let!(:build) do
+ create(:ci_build, pipeline: pipeline,
+ stage: 'test')
+ end
+
+ before do
+ build.run
+ visit_project_pipelines
+ end
+
+ it 'indicates that pipeline can be canceled' do
+ expect(page).to have_selector('.js-pipelines-cancel-button')
+ expect(page).to have_selector('.ci-running')
+ end
+
+ context 'when canceling' do
+ before do
+ find('.js-pipelines-cancel-button').click
+ click_button 'Stop pipeline'
+ wait_for_requests
+ end
+
+ it 'indicated that pipelines was canceled', :sidekiq_might_not_need_inline do
+ expect(page).not_to have_selector('.js-pipelines-cancel-button')
+ expect(page).to have_selector('.ci-canceled')
+ end
+ end
+ end
+
+ context 'when pipeline is retryable', :sidekiq_might_not_need_inline do
+ let!(:build) do
+ create(:ci_build, pipeline: pipeline,
+ stage: 'test')
+ end
+
+ before do
+ build.drop
+ visit_project_pipelines
+ end
+
+ it 'indicates that pipeline can be retried' do
+ expect(page).to have_selector('.js-pipelines-retry-button')
+ expect(page).to have_selector('.ci-failed')
+ end
+
+ context 'when retrying' do
+ before do
+ find('.js-pipelines-retry-button').click
+ wait_for_requests
+ end
+
+ it 'shows running pipeline that is not retryable' do
+ expect(page).not_to have_selector('.js-pipelines-retry-button')
+ expect(page).to have_selector('.ci-running')
+ end
+ end
+ end
+
+ context 'when pipeline is detached merge request pipeline' do
+ let(:merge_request) do
+ create(:merge_request,
+ :with_detached_merge_request_pipeline,
+ source_project: source_project,
+ target_project: target_project)
+ end
+
+ let!(:pipeline) { merge_request.all_pipelines.first }
+ let(:source_project) { project }
+ let(:target_project) { project }
+
+ before do
+ visit project_pipelines_path(source_project)
+ end
+
+ shared_examples_for 'detached merge request pipeline' do
+ it 'shows pipeline information without pipeline ref', :sidekiq_might_not_need_inline do
+ within '.pipeline-tags' do
+ expect(page).to have_content(expected_detached_mr_tag)
+
+ expect(page).to have_link(merge_request.iid,
+ href: project_merge_request_path(project, merge_request))
+
+ expect(page).not_to have_link(pipeline.ref)
+ end
+ end
+ end
+
+ it_behaves_like 'detached merge request pipeline'
+
+ context 'when source project is a forked project' do
+ let(:source_project) { fork_project(project, user, repository: true) }
+
+ it_behaves_like 'detached merge request pipeline'
+ end
+ end
+
+ context 'when pipeline is merge request pipeline' do
+ let(:merge_request) do
+ create(:merge_request,
+ :with_merge_request_pipeline,
+ source_project: source_project,
+ target_project: target_project,
+ merge_sha: target_project.commit.sha)
+ end
+
+ let!(:pipeline) { merge_request.all_pipelines.first }
+ let(:source_project) { project }
+ let(:target_project) { project }
+
+ before do
+ visit project_pipelines_path(source_project)
+ end
+
+ shared_examples_for 'Correct merge request pipeline information' do
+ it 'does not show detached tag for the pipeline, and shows the link of the merge request' \
+ 'and does not show the ref of the pipeline', :sidekiq_might_not_need_inline do
+ within '.pipeline-tags' do
+ expect(page).not_to have_content(expected_detached_mr_tag)
+
+ expect(page).to have_link(merge_request.iid,
+ href: project_merge_request_path(project, merge_request))
+
+ expect(page).not_to have_link(pipeline.ref)
+ end
+ end
+ end
+
+ it_behaves_like 'Correct merge request pipeline information'
+
+ context 'when source project is a forked project' do
+ let(:source_project) { fork_project(project, user, repository: true) }
+
+ it_behaves_like 'Correct merge request pipeline information'
+ end
+ end
+
+ context 'when pipeline has configuration errors' do
+ let(:pipeline) do
+ create(:ci_pipeline, :invalid, project: project)
+ end
+
+ before do
+ visit_project_pipelines
+ end
+
+ it 'contains badge that indicates errors' do
+ expect(page).to have_content 'yaml invalid'
+ end
+
+ it 'contains badge with tooltip which contains error' do
+ expect(pipeline).to have_yaml_errors
+ expect(page).to have_selector(
+ %Q{span[title="#{pipeline.yaml_errors}"]})
+ end
+
+ it 'contains badge that indicates failure reason' do
+ expect(page).to have_content 'error'
+ end
+
+ it 'contains badge with tooltip which contains failure reason' do
+ expect(pipeline.failure_reason?).to eq true
+ expect(page).to have_selector(
+ %Q{span[title="#{pipeline.present.failure_reason}"]})
+ end
+ end
+
+ context 'with manual actions' do
+ let!(:manual) do
+ create(:ci_build, :manual,
+ pipeline: pipeline,
+ name: 'manual build',
+ stage: 'test')
+ end
+
+ before do
+ visit_project_pipelines
+ end
+
+ it 'has a dropdown with play button' do
+ expect(page).to have_selector('[data-testid="pipelines-manual-actions-dropdown"] [data-testid="play-icon"]')
+ end
+
+ it 'has link to the manual action' do
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
+
+ expect(page).to have_button('manual build')
+ end
+
+ context 'when manual action was played' do
+ before do
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
+ click_button('manual build')
+ end
+
+ it 'enqueues manual action job' do
+ expect(page).to have_selector(
+ '[data-testid="pipelines-manual-actions-dropdown"] .gl-dropdown-toggle:disabled'
+ )
+ end
+ end
+ end
+
+ context 'when there is a delayed job' do
+ let!(:delayed_job) do
+ create(:ci_build, :scheduled,
+ pipeline: pipeline,
+ name: 'delayed job 1',
+ stage: 'test')
+ end
+
+ before do
+ stub_feature_flags(bootstrap_confirmation_modals: false)
+ visit_project_pipelines
+ end
+
+ it 'has a dropdown for actionable jobs' do
+ expect(page).to have_selector('[data-testid="pipelines-manual-actions-dropdown"] [data-testid="play-icon"]')
+ end
+
+ it "has link to the delayed job's action" do
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
+
+ time_diff = [0, delayed_job.scheduled_at - Time.zone.now].max
+ expect(page).to have_button('delayed job 1')
+ expect(page).to have_content(Time.at(time_diff).utc.strftime("%H:%M:%S"))
+ end
+
+ context 'when delayed job is expired already' do
+ let!(:delayed_job) do
+ create(:ci_build, :expired_scheduled,
+ pipeline: pipeline,
+ name: 'delayed job 1',
+ stage: 'test')
+ end
+
+ it "shows 00:00:00 as the remaining time" do
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
+
+ expect(page).to have_content("00:00:00")
+ end
+ end
+
+ context 'when user played a delayed job immediately' do
+ before do
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
+ accept_gl_confirm do
+ click_button 'delayed job 1'
+ end
+ wait_for_requests
+ end
+
+ it 'enqueues the delayed job', :js do
+ expect(delayed_job.reload).to be_pending
+ end
+ end
+ end
+
+ context 'for generic statuses' do
+ context 'when preparing' do
+ let!(:pipeline) do
+ create(:ci_empty_pipeline,
+ status: 'preparing', project: project)
+ end
+
+ let!(:status) do
+ create(:generic_commit_status,
+ :preparing, pipeline: pipeline)
+ end
+
+ before do
+ visit_project_pipelines
+ end
+
+ it 'is cancelable' do
+ expect(page).to have_selector('.js-pipelines-cancel-button')
+ end
+
+ it 'shows the pipeline as preparing' do
+ expect(page).to have_selector('.ci-preparing')
+ end
+ end
+
+ context 'when running' do
+ let!(:running) do
+ create(:generic_commit_status,
+ status: 'running',
+ pipeline: pipeline,
+ stage: 'test')
+ end
+
+ before do
+ visit_project_pipelines
+ end
+
+ it 'is cancelable' do
+ expect(page).to have_selector('.js-pipelines-cancel-button')
+ end
+
+ it 'has pipeline running' do
+ expect(page).to have_selector('.ci-running')
+ end
+
+ context 'when canceling' do
+ before do
+ find('.js-pipelines-cancel-button').click
+ click_button 'Stop pipeline'
+ end
+
+ it 'indicates that pipeline was canceled', :sidekiq_might_not_need_inline do
+ expect(page).not_to have_selector('.js-pipelines-cancel-button')
+ expect(page).to have_selector('.ci-canceled')
+ end
+ end
+ end
+
+ context 'when failed' do
+ let!(:status) do
+ create(:generic_commit_status, :pending,
+ pipeline: pipeline,
+ stage: 'test')
+ end
+
+ before do
+ status.drop
+ visit_project_pipelines
+ end
+
+ it 'is not retryable' do
+ expect(page).not_to have_selector('.js-pipelines-retry-button')
+ end
+
+ it 'has failed pipeline', :sidekiq_might_not_need_inline do
+ expect(page).to have_selector('.ci-failed')
+ end
+ end
+ end
+
+ context 'downloadable pipelines' do
+ context 'with artifacts' do
+ let!(:with_artifacts) do
+ build = create(:ci_build, :success,
+ pipeline: pipeline,
+ name: 'rspec tests',
+ stage: 'test')
+
+ create(:ci_job_artifact, :codequality, job: build)
+ end
+
+ before do
+ visit_project_pipelines
+ end
+
+ it 'has artifacts dropdown' do
+ expect(page).to have_selector('[data-testid="pipeline-multi-actions-dropdown"]')
+ end
+ end
+
+ context 'with artifacts expired' do
+ let!(:with_artifacts_expired) do
+ create(:ci_build, :expired, :success,
+ pipeline: pipeline,
+ name: 'rspec',
+ stage: 'test')
+ end
+
+ before do
+ visit_project_pipelines
+ end
+
+ it { expect(page).not_to have_selector('[data-testid="artifact-item"]') }
+ end
+
+ context 'without artifacts' do
+ let!(:without_artifacts) do
+ create(:ci_build, :success,
+ pipeline: pipeline,
+ name: 'rspec',
+ stage: 'test')
+ end
+
+ before do
+ visit_project_pipelines
+ end
+
+ it { expect(page).not_to have_selector('[data-testid="artifact-item"]') }
+ end
+
+ context 'with trace artifact' do
+ before do
+ create(:ci_build, :success, :trace_artifact, pipeline: pipeline)
+
+ visit_project_pipelines
+ end
+
+ it 'does not show trace artifact as artifacts' do
+ expect(page).not_to have_selector('[data-testid="artifact-item"]')
+ end
+ end
+ end
+
+ context 'mini pipeline graph' do
+ let!(:build) do
+ create(:ci_build, :pending, pipeline: pipeline,
+ stage: 'build',
+ name: 'build')
+ end
+
+ dropdown_selector = '[data-testid="mini-pipeline-graph-dropdown"]'
+
+ before do
+ visit_project_pipelines
+ end
+
+ it 'renders a mini pipeline graph' do
+ expect(page).to have_selector('[data-testid="pipeline-mini-graph"]')
+ expect(page).to have_selector(dropdown_selector)
+ end
+
+ context 'when clicking a stage badge' do
+ it 'opens a dropdown' do
+ find(dropdown_selector).click
+
+ expect(page).to have_link build.name
+ end
+
+ it 'is possible to cancel pending build' do
+ find(dropdown_selector).click
+ find('.js-ci-action').click
+ wait_for_requests
+
+ expect(build.reload).to be_canceled
+ end
+ end
+
+ context 'for a failed pipeline' do
+ let!(:build) do
+ create(:ci_build, :failed, pipeline: pipeline,
+ stage: 'build',
+ name: 'build')
+ end
+
+ it 'displays the failure reason' do
+ find(dropdown_selector).click
+
+ within('.js-builds-dropdown-list') do
+ build_element = page.find('.mini-pipeline-graph-dropdown-item')
+ expect(build_element['title']).to eq('build - failed - (unknown failure)')
+ end
+ end
+ end
+ end
+
+ context 'with pagination' do
+ before do
+ allow(Ci::Pipeline).to receive(:default_per_page).and_return(1)
+ create(:ci_empty_pipeline, project: project)
+ end
+
+ it 'renders pagination' do
+ visit project_pipelines_path(project)
+ wait_for_requests
+
+ expect(page).to have_selector('.gl-pagination')
+ end
+
+ it 'renders second page of pipelines' do
+ visit project_pipelines_path(project, page: '2')
+ wait_for_requests
+
+ expect(page).to have_selector('.gl-pagination .page-link', count: 4)
+ end
+
+ it 'shows updated content' do
+ visit project_pipelines_path(project)
+ wait_for_requests
+ page.find('.page-link.next-page-item').click
+
+ expect(page).to have_selector('.gl-pagination .page-link', count: 4)
+ end
+ end
+
+ context 'with pipeline key selection' do
+ before do
+ visit project_pipelines_path(project)
+ wait_for_requests
+ end
+
+ it 'changes the Pipeline ID column for Pipeline IID' do
+ page.find('[data-testid="pipeline-key-dropdown"]').click
+
+ within '.gl-new-dropdown-contents' do
+ dropdown_options = page.find_all '.gl-new-dropdown-item'
+
+ dropdown_options[1].click
+ end
+
+ expect(page.find('[data-testid="pipeline-th"]')).to have_content 'Pipeline'
+ expect(page.find('[data-testid="pipeline-url-link"]')).to have_content "##{pipeline.iid}"
+ end
+ end
+ end
+
+ describe 'GET /:project/-/pipelines/show' do
+ let(:project) { create(:project, :repository) }
+
+ let(:pipeline) do
+ create(:ci_empty_pipeline,
+ project: project,
+ sha: project.commit.id,
+ user: user)
+ end
+
+ before do
+ create_build('build', 0, 'build', :success)
+ create_build('test', 1, 'rspec 0:2', :pending)
+ create_build('test', 1, 'rspec 1:2', :running)
+ create_build('test', 1, 'spinach 0:2', :created)
+ create_build('test', 1, 'spinach 1:2', :created)
+ create_build('test', 1, 'audit', :created)
+ create_build('deploy', 2, 'production', :created)
+
+ create(
+ :generic_commit_status,
+ pipeline: pipeline,
+ stage: 'external',
+ name: 'jenkins',
+ stage_idx: 3,
+ ref: 'master'
+ )
+
+ visit project_pipeline_path(project, pipeline)
+ wait_for_requests
+ end
+
+ it 'shows a graph with grouped stages' do
+ expect(page).to have_css('.js-pipeline-graph')
+
+ # header
+ expect(page).to have_text("##{pipeline.id}")
+ expect(page).to have_selector(%Q(img[src="#{pipeline.user.avatar_url}"]))
+ expect(page).to have_link(pipeline.user.name, href: user_path(pipeline.user))
+
+ # stages
+ expect(page).to have_text('Build')
+ expect(page).to have_text('Test')
+ expect(page).to have_text('Deploy')
+ expect(page).to have_text('External')
+
+ # builds
+ expect(page).to have_text('rspec')
+ expect(page).to have_text('spinach')
+ expect(page).to have_text('rspec')
+ expect(page).to have_text('production')
+ expect(page).to have_text('jenkins')
+ end
+
+ def create_build(stage, stage_idx, name, status)
+ create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name, status: status)
+ end
+ end
+
+ describe 'POST /:project/-/pipelines' do
+ let(:project) { create(:project, :repository) }
+
+ before do
+ visit new_project_pipeline_path(project)
+ end
+
+ context 'for valid commit', :js do
+ before do
+ click_button project.default_branch
+ wait_for_requests
+
+ find('p', text: 'master').click
+ wait_for_requests
+ end
+
+ context 'with gitlab-ci.yml', :js do
+ before do
+ stub_ci_pipeline_to_return_yaml_file
+ end
+
+ it 'creates a new pipeline' do
+ expect do
+ click_on 'Run pipeline'
+ wait_for_requests
+ end
+ .to change { Ci::Pipeline.count }.by(1)
+
+ expect(Ci::Pipeline.last).to be_web
+ end
+
+ context 'when variables are specified' do
+ it 'creates a new pipeline with variables' do
+ 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 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) })
+ .to eq [{ key: "key_name", secret_value: "value" }.with_indifferent_access]
+ end
+ end
+ end
+
+ 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
+ stub_ci_pipeline_to_return_yaml_file
+
+ expect do
+ click_on 'Run pipeline'
+ wait_for_requests
+ end
+ .to change { Ci::Pipeline.count }.by(1)
+ end
+ end
+ end
+ end
+
+ describe 'Reset runner caches' do
+ let(:project) { create(:project, :repository) }
+
+ before do
+ create(:ci_empty_pipeline, status: 'success', project: project, sha: project.commit.id, ref: 'master')
+ project.add_maintainer(user)
+ visit project_pipelines_path(project)
+ end
+
+ it 'has a clear caches button' do
+ expect(page).to have_button 'Clear runner caches'
+ end
+
+ describe 'user clicks the button' do
+ context 'when project already has jobs_cache_index' do
+ before do
+ project.update!(jobs_cache_index: 1)
+ end
+
+ it 'increments jobs_cache_index' do
+ click_button 'Clear runner caches'
+ wait_for_requests
+ expect(page.find('[data-testid="alert-info"]')).to have_content 'Project cache successfully reset.'
+ end
+ end
+
+ context 'when project does not have jobs_cache_index' do
+ it 'sets jobs_cache_index to 1' do
+ click_button 'Clear runner caches'
+ wait_for_requests
+ expect(page.find('[data-testid="alert-info"]')).to have_content 'Project cache successfully reset.'
+ end
+ end
+ end
+ end
+
+ describe 'Run Pipelines' do
+ let(:project) { create(:project, :repository) }
+
+ before do
+ 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('[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
+
+ describe 'find pipelines' do
+ it 'shows filtered pipelines', :js do
+ click_button project.default_branch
+
+ page.within '[data-testid="ref-select"]' do
+ find('[data-testid="search-refs"]').native.send_keys('fix')
+
+ page.within '.gl-new-dropdown-contents' do
+ expect(page).to have_content('fix')
+ end
+ end
+ end
+ end
+ end
+
+ describe 'Empty State' do
+ let(:project) { create(:project, :repository) }
+
+ before do
+ visit project_pipelines_path(project)
+ end
+
+ it 'renders empty state' do
+ expect(page).to have_content 'Try test template'
+ end
+ end
+ end
+
+ context 'when user is not logged in' do
+ before do
+ project.update!(auto_devops_attributes: { enabled: false })
+ visit project_pipelines_path(project)
+ end
+
+ context 'when project is public' do
+ let(:project) { create(:project, :public, :repository) }
+
+ context 'without pipelines' do
+ it { expect(page).to have_content 'This project is not currently set up to run pipelines.' }
+ end
+ end
+
+ context 'when project is private' do
+ let(:project) { create(:project, :private, :repository) }
+
+ it 'redirects the user to sign_in and displays the flash alert' do
+ expect(page).to have_content 'You need to sign in'
+ expect(page).to have_current_path("/users/sign_in")
+ end
+ end
+ end
+
+ def visit_project_pipelines(**query)
+ visit project_pipelines_path(project, query)
+ wait_for_requests
+ end
+end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 219c8ec0070..9eda05f695d 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -15,7 +15,6 @@ RSpec.describe 'Pipeline', :js do
before do
sign_in(user)
project.add_role(user, role)
- stub_feature_flags(pipeline_tabs_vue: false)
end
shared_context 'pipeline builds' do
@@ -80,12 +79,6 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('Cancel running')
end
- it 'shows Pipeline tab pane as active' do
- visit_pipeline
-
- expect(page).to have_css('#js-tab-pipeline.active')
- end
-
it 'shows link to the pipeline ref' do
visit_pipeline
@@ -190,11 +183,11 @@ RSpec.describe 'Pipeline', :js do
end
describe 'pipeline graph' do
- before do
- visit_pipeline
- end
-
context 'when pipeline has running builds' do
+ before do
+ visit_pipeline
+ end
+
it 'shows a running icon and a cancel action for the running build' do
page.within('#ci-badge-deploy') do
expect(page).to have_selector('.js-ci-status-icon-running')
@@ -213,6 +206,10 @@ RSpec.describe 'Pipeline', :js do
end
context 'when pipeline has preparing builds' do
+ before do
+ visit_pipeline
+ end
+
it 'shows a preparing icon and a cancel action' do
page.within('#ci-badge-prepare') do
expect(page).to have_selector('.js-ci-status-icon-preparing')
@@ -231,6 +228,10 @@ RSpec.describe 'Pipeline', :js do
end
context 'when pipeline has successful builds' do
+ before do
+ visit_pipeline
+ end
+
it 'shows the success icon and a retry action for the successful build' do
page.within('#ci-badge-build') do
expect(page).to have_selector('.js-ci-status-icon-success')
@@ -254,6 +255,10 @@ RSpec.describe 'Pipeline', :js do
end
context 'when pipeline has a delayed job' do
+ before do
+ visit_pipeline
+ end
+
let(:project) { create(:project, :repository, group: group) }
it 'shows the scheduled icon and an unschedule action for the delayed job' do
@@ -277,6 +282,10 @@ RSpec.describe 'Pipeline', :js do
end
context 'when pipeline has failed builds' do
+ before do
+ visit_pipeline
+ end
+
it 'shows the failed icon and a retry action for the failed build' do
page.within('#ci-badge-test') do
expect(page).to have_selector('.js-ci-status-icon-failed')
@@ -307,6 +316,10 @@ RSpec.describe 'Pipeline', :js do
end
context 'when pipeline has manual jobs' do
+ before do
+ visit_pipeline
+ end
+
it 'shows the skipped icon and a play action for the manual build' do
page.within('#ci-badge-manual-build') do
expect(page).to have_selector('.js-ci-status-icon-manual')
@@ -330,12 +343,139 @@ RSpec.describe 'Pipeline', :js do
end
context 'when pipeline has external job' do
+ before do
+ visit_pipeline
+ end
+
it 'shows the success icon and the generic comit status build' do
expect(page).to have_selector('.js-ci-status-icon-success')
expect(page).to have_content('jenkins')
expect(page).to have_link('jenkins', href: 'http://gitlab.com/status')
end
end
+
+ context 'when pipeline has a downstream pipeline' do
+ let(:downstream_project) { create(:project, :repository, group: group) }
+ let(:downstream_pipeline) do
+ create(:ci_pipeline,
+ status,
+ user: user,
+ project: downstream_project,
+ ref: 'master',
+ sha: downstream_project.commit.id,
+ child_of: pipeline )
+ end
+
+ let!(:build) { create(:ci_build, status, pipeline: downstream_pipeline, user: user) }
+
+ before do
+ downstream_pipeline.project.add_developer(user)
+ end
+
+ context 'and user has permission' do
+ before do
+ visit_pipeline
+ end
+
+ context 'with a successful downstream' do
+ let(:status) { :success }
+
+ it 'does not show the cancel or retry action' do
+ expect(page).to have_selector('.ci-status-icon-success')
+ expect(page).not_to have_selector('button[aria-label="Retry downstream pipeline"]')
+ expect(page).not_to have_selector('button[aria-label="Cancel downstream pipeline"]')
+ end
+ end
+
+ context 'with a running downstream' do
+ let(:status) { :running }
+
+ it 'shows the cancel action' do
+ expect(page).to have_selector('button[aria-label="Cancel downstream pipeline"]')
+ end
+
+ context 'when canceling' do
+ before do
+ find('button[aria-label="Cancel downstream pipeline"]').click
+ wait_for_requests
+ end
+
+ it 'shows the pipeline as canceled with the retry action' do
+ expect(page).to have_selector('button[aria-label="Retry downstream pipeline"]')
+ expect(page).to have_selector('.ci-status-icon-canceled')
+ end
+ end
+ end
+
+ context 'with a failed downstream' do
+ let(:status) { :failed }
+
+ it 'indicates that pipeline can be retried' do
+ expect(page).to have_selector('button[aria-label="Retry downstream pipeline"]')
+ end
+
+ context 'and the FF downstream_retry_action is disabled' do
+ before do
+ stub_feature_flags(downstream_retry_action: false)
+ end
+
+ it 'does not show the retry action' do
+ expect(page).not_to have_selector('button[aria-label="Retry downstream pipeline"]')
+ end
+ end
+
+ context 'when retrying' do
+ before do
+ find('button[aria-label="Retry downstream pipeline"]').click
+ wait_for_requests
+ end
+
+ it 'shows running pipeline with the cancel action' do
+ expect(page).to have_selector('.ci-status-icon-running')
+ expect(page).to have_selector('button[aria-label="Cancel downstream pipeline"]')
+ end
+ end
+ end
+
+ context 'with a canceled downstream' do
+ let(:status) { :canceled }
+
+ it 'indicates that pipeline can be retried' do
+ expect(page).to have_selector('button[aria-label="Retry downstream pipeline"]')
+ end
+
+ context 'when retrying' do
+ before do
+ find('button[aria-label="Retry downstream pipeline"]').click
+ wait_for_requests
+ end
+
+ it 'shows running pipeline with the cancel action' do
+ expect(page).to have_selector('.ci-status-icon-running')
+ expect(page).to have_selector('button[aria-label="Cancel downstream pipeline"]')
+ end
+ end
+ end
+ end
+
+ context 'when user does not have permissions' do
+ let(:status) { :failed }
+
+ before do
+ new_user = create(:user)
+ project.add_role(new_user, :guest)
+ downstream_project.add_role(new_user, :guest)
+ sign_in(new_user)
+
+ visit_pipeline
+ end
+
+ it 'does not show the retry button' do
+ expect(page).to have_selector('.ci-status-icon-failed')
+ expect(page).not_to have_selector('button[aria-label="Retry downstream pipeline"]')
+ end
+ end
+ end
end
context 'when the pipeline has manual stage' do
@@ -357,7 +497,6 @@ RSpec.describe 'Pipeline', :js do
context 'page tabs' do
before do
- stub_feature_flags(pipeline_tabs_vue: false)
visit_pipeline
end
@@ -369,13 +508,10 @@ RSpec.describe 'Pipeline', :js do
end
it 'shows counter in Jobs tab' do
+ skip('Enable in jobs `pipeline_tabs_vue` MR')
expect(page.find('.js-builds-counter').text).to eq(pipeline.total_size.to_s)
end
- it 'shows Pipeline tab as active' do
- expect(page).to have_css('.js-pipeline-tab-link .active')
- end
-
context 'without permission to access builds' do
let(:project) { create(:project, :public, :repository, public_builds: false) }
let(:role) { :guest }
@@ -753,6 +889,7 @@ RSpec.describe 'Pipeline', :js do
describe 'GET /:project/-/pipelines/:id/builds' do
before do
+ stub_feature_flags(pipeline_tabs_vue: false)
visit builds_project_pipeline_path(project, pipeline)
end
@@ -893,28 +1030,6 @@ RSpec.describe 'Pipeline', :js do
end
end
end
-
- context 'when deploy job is a bridge to trigger a downstream pipeline' do
- let!(:deploy_job) do
- create(:ci_bridge, :created, stage: 'deploy', name: 'deploy',
- stage_idx: 2, pipeline: pipeline, project: project, resource_group: resource_group)
- end
-
- it 'shows deploy job as waiting for resource' do
- subject
-
- within('.js-pipeline-header-container') do
- expect(page).to have_content('waiting')
- end
-
- within('.js-pipeline-graph') do
- within(all('[data-testid="stage-column"]')[1]) do
- expect(page).to have_content('deploy')
- expect(page).to have_css('.ci-status-icon-waiting-for-resource')
- end
- end
- end
- end
end
end
end
@@ -943,15 +1058,7 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_button('Play')
end
- it 'shows jobs tab pane as active' do
- expect(page).to have_css('#js-tab-builds.active')
- end
-
context 'page tabs' do
- before do
- stub_feature_flags(pipeline_tabs_vue: false)
- end
-
it 'shows Pipeline, Jobs and DAG tabs with link' do
expect(page).to have_link('Pipeline')
expect(page).to have_link('Jobs')
@@ -959,12 +1066,9 @@ RSpec.describe 'Pipeline', :js do
end
it 'shows counter in Jobs tab' do
+ skip('unskip when jobs tab is implemented with ff `pipeline_tabs_vue`')
expect(page.find('.js-builds-counter').text).to eq(pipeline.total_size.to_s)
end
-
- it 'shows Jobs tab as active' do
- expect(page).to have_css('li.js-builds-tab-link .active')
- end
end
context 'retrying jobs' do
@@ -1022,14 +1126,14 @@ RSpec.describe 'Pipeline', :js do
end
describe 'GET /:project/-/pipelines/:id/failures' do
- before do
- stub_feature_flags(pipeline_tabs_vue: false)
- end
-
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: '1234') }
let(:pipeline_failures_page) { failures_project_pipeline_path(project, pipeline) }
let!(:failed_build) { create(:ci_build, :failed, pipeline: pipeline) }
+ before do
+ stub_feature_flags(pipeline_tabs_vue: false)
+ end
+
subject { visit pipeline_failures_page }
context 'with failed build' do
@@ -1037,13 +1141,6 @@ RSpec.describe 'Pipeline', :js do
failed_build.trace.set('4 examples, 1 failure')
end
- it 'shows jobs tab pane as active' do
- subject
-
- expect(page).to have_content('Failed Jobs')
- expect(page).to have_css('#js-tab-failures.active')
- end
-
it 'lists failed builds' do
subject
@@ -1063,12 +1160,43 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('There is an unknown failure, please try again')
end
+ context 'when failed_jobs_tab_vue feature flag is disabled' do
+ before do
+ stub_feature_flags(failed_jobs_tab_vue: false)
+ end
+
+ context 'when user does not have permission to retry build' do
+ it 'shows retry button for failed build' do
+ subject
+
+ page.within(find('.build-failures', match: :first)) do
+ expect(page).not_to have_link('Retry')
+ end
+ end
+ end
+
+ context 'when user does have permission to retry build' do
+ before do
+ create(:protected_branch, :developers_can_merge,
+ name: pipeline.ref, project: project)
+ end
+
+ it 'shows retry button for failed build' do
+ subject
+
+ page.within(find('.build-failures', match: :first)) do
+ expect(page).to have_link('Retry')
+ end
+ end
+ end
+ end
+
context 'when user does not have permission to retry build' do
it 'shows retry button for failed build' do
subject
- page.within(find('.build-failures', match: :first)) do
- expect(page).not_to have_link('Retry')
+ page.within(find('#js-tab-failures', match: :first)) do
+ expect(page).not_to have_button('Retry')
end
end
end
@@ -1082,21 +1210,14 @@ RSpec.describe 'Pipeline', :js do
it 'shows retry button for failed build' do
subject
- page.within(find('.build-failures', match: :first)) do
- expect(page).to have_link('Retry')
+ page.within(find('#js-tab-failures', match: :first)) do
+ expect(page).to have_button('Retry')
end
end
end
end
context 'when missing build logs' do
- it 'shows jobs tab pane as active' do
- subject
-
- expect(page).to have_content('Failed Jobs')
- expect(page).to have_css('#js-tab-failures.active')
- end
-
it 'lists failed builds' do
subject
@@ -1133,11 +1254,17 @@ RSpec.describe 'Pipeline', :js do
failed_build.update!(status: :success)
end
+ it 'does not show the failure tab' do
+ skip('unskip when the failure tab has been implemented in ff `pipeline_tabs_vue`')
+ subject
+
+ expect(page).not_to have_content('Failed Jobs')
+ end
+
it 'displays the pipeline graph' do
subject
expect(page).to have_current_path(pipeline_path(pipeline), ignore_query: true)
- expect(page).not_to have_content('Failed Jobs')
expect(page).to have_selector('.js-pipeline-graph')
end
end
@@ -1151,27 +1278,14 @@ RSpec.describe 'Pipeline', :js do
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
before do
- stub_feature_flags(pipeline_tabs_vue: false)
visit dag_project_pipeline_path(project, pipeline)
end
- it 'shows DAG tab pane as active' do
- expect(page).to have_css('#js-tab-dag.active', visible: false)
- end
-
context 'page tabs' do
it 'shows Pipeline, Jobs and DAG tabs with link' do
expect(page).to have_link('Pipeline')
expect(page).to have_link('Jobs')
- expect(page).to have_link('DAG')
- end
-
- it 'shows counter in Jobs tab' do
- expect(page.find('.js-builds-counter').text).to eq(pipeline.total_size.to_s)
- end
-
- it 'shows DAG tab as active' do
- expect(page).to have_css('li.js-dag-tab-link .active')
+ expect(page).to have_link('Needs')
end
end
end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 8b1a22ae05a..a18bf7c5caf 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -623,7 +623,6 @@ RSpec.describe 'Pipelines', :js do
create(:generic_commit_status, pipeline: pipeline, stage: 'external', name: 'jenkins', stage_idx: 3, ref: 'master')
- stub_feature_flags(pipeline_tabs_vue: false)
visit project_pipeline_path(project, pipeline)
wait_for_requests
end
@@ -794,12 +793,29 @@ RSpec.describe 'Pipelines', :js do
describe 'Empty State' do
let(:project) { create(:project, :repository) }
- before do
- visit project_pipelines_path(project)
+ context 'when `ios_specific_templates` is not enabled' do
+ before do
+ visit project_pipelines_path(project)
+ end
+
+ it 'renders empty state' do
+ expect(page).to have_content 'Try test template'
+ end
end
- it 'renders empty state' do
- expect(page).to have_content 'Try test template'
+ describe 'when the `ios_specific_templates` experiment is enabled and the "Set up a runner" button is clicked' do
+ before do
+ stub_experiments(ios_specific_templates: :candidate)
+ create(:project_setting, project: project, target_platforms: %w(ios))
+ visit project_pipelines_path(project)
+ click_button 'Set up a runner'
+ end
+
+ it 'displays a modal with the macOS platform selected and an explanation popover' do
+ expect(page).to have_button 'macOS', class: 'selected'
+ expect(page).to have_selector('#runner-instructions-modal___BV_modal_content_')
+ expect(page).to have_selector('.popover')
+ end
end
end
end
diff --git a/spec/features/projects/serverless/functions_spec.rb b/spec/features/projects/serverless/functions_spec.rb
deleted file mode 100644
index db8c2a24f2f..00000000000
--- a/spec/features/projects/serverless/functions_spec.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Functions', :js do
- include KubernetesHelpers
- include ReactiveCachingHelpers
-
- let(:project) { create(:project, :repository) }
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- gitlab_sign_in(user)
- end
-
- shared_examples "it's missing knative installation" do
- before do
- functions_finder = Projects::Serverless::FunctionsFinder.new(project)
- visit project_serverless_functions_path(project)
- allow(Projects::Serverless::FunctionsFinder)
- .to receive(:new)
- .and_return(functions_finder)
- synchronous_reactive_cache(functions_finder)
- end
-
- it 'sees an empty state require Knative installation' do
- expect(page).to have_selector('.empty-state')
- end
- end
-
- context 'when user does not have a cluster and visits the serverless page' do
- it_behaves_like "it's missing knative installation"
- end
-
- context 'when the user does have a cluster and visits the serverless page' do
- let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
-
- it_behaves_like "it's missing knative installation"
- end
-
- context 'when the user has a cluster and knative installed and visits the serverless page', :kubeclient do
- let(:cluster) { create(:cluster, :project, :provided_by_gcp, projects: [project]) }
- let(:service) { cluster.platform_kubernetes }
- let(:environment) { create(:environment, project: project) }
- let!(:deployment) { create(:deployment, :success, cluster: cluster, environment: environment) }
- let(:knative_services_finder) { environment.knative_services_finder }
- let(:namespace) do
- create(:cluster_kubernetes_namespace,
- cluster: cluster,
- project: cluster.cluster_project.project,
- environment: environment)
- end
-
- before do
- allow(Clusters::KnativeServicesFinder)
- .to receive(:new)
- .and_return(knative_services_finder)
- synchronous_reactive_cache(knative_services_finder)
- stub_kubeclient_knative_services(stub_get_services_options)
- stub_kubeclient_service_pods(nil, namespace: namespace.namespace)
- visit project_serverless_functions_path(project)
- end
-
- context 'when there are no functions' do
- let(:stub_get_services_options) do
- {
- namespace: namespace.namespace,
- response: kube_response({ "kind" => "ServiceList", "items" => [] })
- }
- end
-
- it 'sees an empty listing of serverless functions' do
- expect(page).to have_selector('.empty-state')
- expect(page).not_to have_selector('.content-list')
- end
- end
-
- context 'when there are functions' do
- let(:stub_get_services_options) { { namespace: namespace.namespace } }
-
- it 'does not see an empty listing of serverless functions' do
- expect(page).not_to have_selector('.empty-state')
- expect(page).to have_selector('.content-list')
- end
- end
- end
-end
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index 39c4315bf0f..a64f81430d1 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -25,24 +25,6 @@ RSpec.describe "Projects > Settings > Pipelines settings" do
context 'for maintainer' do
let(:role) { :maintainer }
- it 'be allowed to change' do
- visit project_settings_ci_cd_path(project)
-
- fill_in('Test coverage parsing', with: 'coverage_regex')
-
- page.within '#js-general-pipeline-settings' do
- click_on 'Save changes'
- end
-
- expect(page.status_code).to eq(200)
-
- page.within '#js-general-pipeline-settings' do
- expect(page).to have_button('Save changes', disabled: false)
- end
-
- expect(page).to have_field('Test coverage parsing', with: 'coverage_regex')
- end
-
it 'updates auto_cancel_pending_pipelines' do
visit project_settings_ci_cd_path(project)
diff --git a/spec/features/projects/settings/secure_files_settings_spec.rb b/spec/features/projects/settings/secure_files_settings_spec.rb
new file mode 100644
index 00000000000..c7c9cafc420
--- /dev/null
+++ b/spec/features/projects/settings/secure_files_settings_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Secure Files Settings' do
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:project) { create(:project, creator_id: maintainer.id) }
+
+ before_all do
+ project.add_maintainer(maintainer)
+ end
+
+ context 'when the :ci_secure_files feature flag is enabled' do
+ before do
+ stub_feature_flags(ci_secure_files: true)
+
+ sign_in(user)
+ visit project_settings_ci_cd_path(project)
+ end
+
+ context 'authenticated user with admin permissions' do
+ let(:user) { maintainer }
+
+ it 'shows the secure files settings' do
+ expect(page).to have_content('Secure Files')
+ end
+ end
+ end
+
+ context 'when the :ci_secure_files feature flag is disabled' do
+ before do
+ stub_feature_flags(ci_secure_files: false)
+
+ sign_in(user)
+ visit project_settings_ci_cd_path(project)
+ end
+
+ context 'authenticated user with admin permissions' do
+ let(:user) { maintainer }
+
+ it 'does not shows the secure files settings' do
+ expect(page).not_to have_content('Secure Files')
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
index 77be351f3d8..6aa59f72d2a 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
@@ -50,7 +50,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
context 'when Pipelines are initially enabled' do
it 'shows the Merge Requests settings' do
expect(page).to have_content 'Pipelines must succeed'
- expect(page).to have_content 'All discussions must be resolved'
+ expect(page).to have_content 'All threads must be resolved'
within('.sharing-permissions-form') do
find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .gl-toggle').click
@@ -58,7 +58,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
end
expect(page).not_to have_content 'Pipelines must succeed'
- expect(page).not_to have_content 'All discussions must be resolved'
+ expect(page).not_to have_content 'All threads must be resolved'
end
end
@@ -70,7 +70,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
it 'shows the Merge Requests settings that do not depend on Builds feature' do
expect(page).to have_content 'Pipelines must succeed'
- expect(page).to have_content 'All discussions must be resolved'
+ expect(page).to have_content 'All threads must be resolved'
within('.sharing-permissions-form') do
find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .gl-toggle').click
@@ -78,7 +78,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
end
expect(page).to have_content 'Pipelines must succeed'
- expect(page).to have_content 'All discussions must be resolved'
+ expect(page).to have_content 'All threads must be resolved'
end
end
end
@@ -91,7 +91,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
it 'does not show the Merge Requests settings' do
expect(page).not_to have_content 'Pipelines must succeed'
- expect(page).not_to have_content 'All discussions must be resolved'
+ expect(page).not_to have_content 'All threads must be resolved'
within('.sharing-permissions-form') do
find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .gl-toggle').click
@@ -99,7 +99,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
end
expect(page).to have_content 'Pipelines must succeed'
- expect(page).to have_content 'All discussions must be resolved'
+ expect(page).to have_content 'All threads must be resolved'
end
end
diff --git a/spec/features/projects/settings/user_manages_project_members_spec.rb b/spec/features/projects/settings/user_manages_project_members_spec.rb
index 2fe06414b32..1d258582b3a 100644
--- a/spec/features/projects/settings/user_manages_project_members_spec.rb
+++ b/spec/features/projects/settings/user_manages_project_members_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Projects > Settings > User manages project members' do
include Spec::Support::Helpers::ModalHelpers
let(:group) { create(:group, name: 'OpenSource') }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :with_namespace_settings) }
let(:project2) { create(:project) }
let(:user) { create(:user) }
let(:user_dmitriy) { create(:user, name: 'Dmitriy') }
diff --git a/spec/features/projects/show/user_uploads_files_spec.rb b/spec/features/projects/show/user_uploads_files_spec.rb
index 92b54d83ef3..a222d6b42ab 100644
--- a/spec/features/projects/show/user_uploads_files_spec.rb
+++ b/spec/features/projects/show/user_uploads_files_spec.rb
@@ -55,16 +55,4 @@ RSpec.describe 'Projects > Show > User uploads files' do
include_examples 'uploads and commits a new text file via "upload file" button', drop: value
end
end
-
- context 'with a nonempty repo' do
- let(:project) { create(:project, :repository, creator: user) }
-
- before do
- visit(project_path(project))
- end
-
- [true, false].each do |value|
- include_examples 'uploads and commits a new text file via "upload file" button', drop: value
- end
- end
end
diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb
index 0ed9e23c7f8..cbdf6d6852e 100644
--- a/spec/features/projects/snippets/create_snippet_spec.rb
+++ b/spec/features/projects/snippets/create_snippet_spec.rb
@@ -85,13 +85,4 @@ RSpec.describe 'Projects > Snippets > Create Snippet', :js do
expect(page).to have_content('New Snippet')
end
end
-
- it 'does not allow submitting the form without title and content' do
- snippet_fill_in_title(title)
-
- expect(page).not_to have_button('Create snippet')
-
- snippet_fill_in_form(title: title, content: file_content)
- expect(page).to have_button('Create snippet')
- end
end
diff --git a/spec/features/projects/tags/user_views_tags_spec.rb b/spec/features/projects/tags/user_views_tags_spec.rb
index e1962ad3df5..dfb5d5d9221 100644
--- a/spec/features/projects/tags/user_views_tags_spec.rb
+++ b/spec/features/projects/tags/user_views_tags_spec.rb
@@ -2,6 +2,36 @@
require 'spec_helper'
RSpec.describe 'User views tags', :feature do
+ context 'with html' do
+ let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ let(:user) { create(:user) }
+ let(:tag_name) { "stable" }
+ let!(:release) { create(:release, project: project, tag: tag_name) }
+
+ before do
+ project.add_developer(user)
+ project.repository.add_tag(user, tag_name, project.default_branch_or_main)
+
+ sign_in(user)
+ end
+
+ shared_examples 'renders the tag index page' do
+ it do
+ visit project_tags_path(project)
+
+ expect(page).to have_content tag_name
+ end
+ end
+
+ it_behaves_like 'renders the tag index page'
+
+ context 'when tag name contains a slash' do
+ let(:tag_name) { "stable/v0.1" }
+
+ it_behaves_like 'renders the tag index page'
+ end
+ end
+
context 'rss' do
shared_examples 'has access to the tags RSS feed' do
it do
diff --git a/spec/features/projects/tree/tree_show_spec.rb b/spec/features/projects/tree/tree_show_spec.rb
index cd94e6da018..53e89cd2959 100644
--- a/spec/features/projects/tree/tree_show_spec.rb
+++ b/spec/features/projects/tree/tree_show_spec.rb
@@ -81,7 +81,7 @@ RSpec.describe 'Projects tree', :js do
wait_for_requests
page.within('.project-last-commit') do
- expect(page).to have_selector('.user-avatar-link')
+ expect(page).to have_selector('.gl-avatar')
expect(page).to have_content('Merge branch')
end
end
@@ -152,4 +152,18 @@ RSpec.describe 'Projects tree', :js do
end
end
end
+
+ context 'ref switcher' do
+ it 'switches ref to branch' do
+ ref_name = 'feature'
+ visit project_tree_path(project, 'master')
+ first('.js-project-refs-dropdown').click
+
+ page.within '.project-refs-form' do
+ click_link ref_name
+ end
+
+ expect(page).to have_selector '.dropdown-menu-toggle', text: ref_name
+ end
+ end
end
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index 2dddcd62a6c..534da71e39a 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -346,206 +346,4 @@ RSpec.describe 'Runners' do
end
end
end
-
- context 'group runners in group settings' do
- let(:group) { create(:group) }
-
- before do
- group.add_owner(user)
- stub_feature_flags(runner_list_group_view_vue_ui: false)
- end
-
- context 'group with no runners' do
- it 'there are no runners displayed' do
- visit group_settings_ci_cd_path(group)
-
- expect(page).to have_content 'No runners found'
- end
- end
-
- context 'group with a runner' do
- let!(:runner) { create(:ci_runner, :group, groups: [group], description: 'group-runner') }
-
- it 'the runner is visible' do
- visit group_settings_ci_cd_path(group)
-
- expect(page).not_to have_content 'No runners found'
- expect(page).to have_content 'Available runners: 1'
- expect(page).to have_content 'group-runner'
- end
-
- it 'user can pause and resume the group runner' do
- visit group_settings_ci_cd_path(group)
-
- expect(page).to have_link href: pause_group_runner_path(group, runner)
- expect(page).not_to have_link href: resume_group_runner_path(group, runner)
-
- click_link href: pause_group_runner_path(group, runner)
-
- expect(page).not_to have_link href: pause_group_runner_path(group, runner)
- expect(page).to have_link href: resume_group_runner_path(group, runner)
-
- click_link href: resume_group_runner_path(group, runner)
-
- expect(page).to have_link href: pause_group_runner_path(group, runner)
- expect(page).not_to have_link href: resume_group_runner_path(group, runner)
- end
-
- it 'user can view runner details' do
- visit group_settings_ci_cd_path(group)
-
- expect(page).to have_content(runner.display_name)
-
- click_on runner.short_sha
-
- expect(page).to have_content(runner.platform)
- end
-
- it 'user can remove a group runner' do
- visit group_settings_ci_cd_path(group)
-
- all(:link, href: group_runner_path(group, runner))[1].click
-
- expect(page).not_to have_content(runner.display_name)
- end
-
- it 'user edits the runner to be protected' do
- visit group_settings_ci_cd_path(group)
-
- click_link href: edit_group_runner_path(group, runner)
-
- expect(page.find_field('runner[access_level]')).not_to be_checked
-
- check 'runner_access_level'
- click_button 'Save changes'
-
- expect(page).to have_content 'Protected Yes'
- end
-
- context 'when a runner has a tag' do
- before do
- runner.update!(tag_list: ['tag'])
- end
-
- it 'user edits runner not to run untagged jobs' do
- visit group_settings_ci_cd_path(group)
-
- click_link href: edit_group_runner_path(group, runner)
-
- expect(page.find_field('runner[run_untagged]')).to be_checked
-
- uncheck 'runner_run_untagged'
- click_button 'Save changes'
-
- expect(page).to have_content 'Can run untagged jobs No'
- end
- end
- end
-
- context 'group with a project runner' do
- let(:project) { create(:project, group: group) }
- let!(:runner) { create(:ci_runner, :project, projects: [project], description: 'project-runner') }
-
- it 'the runner is visible' do
- visit group_settings_ci_cd_path(group)
-
- expect(page).not_to have_content 'No runners found'
- expect(page).to have_content 'Available runners: 1'
- expect(page).to have_content 'project-runner'
- end
-
- it 'user can pause and resume the project runner' do
- visit group_settings_ci_cd_path(group)
-
- expect(page).to have_link href: pause_group_runner_path(group, runner)
- expect(page).not_to have_link href: resume_group_runner_path(group, runner)
-
- click_link href: pause_group_runner_path(group, runner)
-
- expect(page).not_to have_link href: pause_group_runner_path(group, runner)
- expect(page).to have_link href: resume_group_runner_path(group, runner)
-
- click_link href: resume_group_runner_path(group, runner)
-
- expect(page).to have_link href: pause_group_runner_path(group, runner)
- expect(page).not_to have_link href: resume_group_runner_path(group, runner)
- end
-
- it 'user can view runner details' do
- visit group_settings_ci_cd_path(group)
-
- expect(page).to have_content(runner.display_name)
-
- click_on runner.short_sha
-
- expect(page).to have_content(runner.platform)
- end
-
- it 'user can remove a project runner' do
- visit group_settings_ci_cd_path(group)
-
- all(:link, href: group_runner_path(group, runner))[1].click
-
- expect(page).not_to have_content(runner.display_name)
- end
-
- it 'user edits the runner to be protected' do
- visit group_settings_ci_cd_path(group)
-
- click_link href: edit_group_runner_path(group, runner)
-
- expect(page.find_field('runner[access_level]')).not_to be_checked
-
- check 'runner_access_level'
- click_button 'Save changes'
-
- expect(page).to have_content 'Protected Yes'
- end
-
- context 'when a runner has a tag' do
- before do
- runner.update!(tag_list: ['tag'])
- end
-
- it 'user edits runner not to run untagged jobs' do
- visit group_settings_ci_cd_path(group)
-
- click_link href: edit_group_runner_path(group, runner)
-
- expect(page.find_field('runner[run_untagged]')).to be_checked
-
- uncheck 'runner_run_untagged'
- click_button 'Save changes'
-
- expect(page).to have_content 'Can run untagged jobs No'
- end
- end
- end
-
- context 'group with a multi-project runner' do
- let(:project) { create(:project, group: group) }
- let(:project_2) { create(:project, group: group) }
- let!(:runner) { create(:ci_runner, :project, projects: [project, project_2], description: 'group-runner') }
-
- it 'user cannot remove the project runner' do
- visit group_settings_ci_cd_path(group)
-
- expect(all(:link, href: group_runner_path(group, runner)).length).to eq(1)
- end
- end
-
- context 'filtered search' do
- it 'allows user to search by status and type', :js do
- visit group_settings_ci_cd_path(group)
-
- find('.filtered-search').click
-
- page.within('#js-dropdown-hint') do
- expect(page).to have_content('Status')
- expect(page).to have_content('Type')
- expect(page).not_to have_content('Tag')
- end
- end
- end
- end
end
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index 4012a302196..48cee4b1f19 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe "Internal Project Access" do
include AccessMatchers
- let_it_be(:project, reload: true) { create(:project, :internal, :repository) }
+ let_it_be(:project, reload: true) { create(:project, :internal, :repository, :with_namespace_settings) }
describe "Project should be internal" do
describe '#internal?' do
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index aa34ccce2c1..c06b1e5da54 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -5,7 +5,9 @@ require 'spec_helper'
RSpec.describe "Private Project Access" do
include AccessMatchers
- let_it_be(:project, reload: true) { create(:project, :private, :repository, public_builds: false) }
+ let_it_be(:project, reload: true) do
+ create(:project, :private, :repository, :with_namespace_settings, public_builds: false)
+ end
describe "Project should be private" do
describe '#private?' do
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index abe128c6f78..d2112430638 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -5,7 +5,9 @@ require 'spec_helper'
RSpec.describe "Public Project Access" do
include AccessMatchers
- let_it_be(:project, reload: true) { create(:project, :public, :repository) }
+ let_it_be(:project, reload: true) do
+ create(:project, :public, :repository, :with_namespace_settings)
+ end
describe "Project should be public" do
describe '#public?' do
diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb
index 82fe895d397..c682ad06977 100644
--- a/spec/features/snippets/user_creates_snippet_spec.rb
+++ b/spec/features/snippets/user_creates_snippet_spec.rb
@@ -109,13 +109,22 @@ RSpec.describe 'User creates snippet', :js do
end
end
- it 'validation fails for the first time' do
- fill_in snippet_title_field, with: title
+ it 'shows validation errors' do
+ title_validation_message = _("This field is required.")
+ files_validation_message = _("Snippets can't contain empty files. Ensure all files have content, or delete them.")
- expect(page).not_to have_button('Create snippet')
+ click_button('Create snippet')
+
+ expect(page).to have_content(title_validation_message)
+ expect(page).to have_content(files_validation_message)
+
+ snippet_fill_in_title(title)
+
+ expect(page).not_to have_content(title_validation_message)
snippet_fill_in_form(title: title, content: file_content)
- expect(page).to have_button('Create snippet')
+
+ expect(page).not_to have_content(files_validation_message)
end
it 'previews a snippet with file' do
diff --git a/spec/features/topic_show_spec.rb b/spec/features/topic_show_spec.rb
index 3a9865a6503..196fc34e3ea 100644
--- a/spec/features/topic_show_spec.rb
+++ b/spec/features/topic_show_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'Topic show page' do
- let_it_be(:topic) { create(:topic, name: 'my-topic', description: 'This is **my** topic https://google.com/ :poop: ```\ncode\n```', avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) }
+ let_it_be(:topic) { create(:topic, name: 'my-topic', title: 'My Topic', description: 'This is **my** topic https://google.com/ :poop: ```\ncode\n```', avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) }
context 'when topic does not exist' do
let(:path) { topic_explore_projects_path(topic_name: 'non-existing') }
@@ -20,8 +20,9 @@ RSpec.describe 'Topic show page' do
visit topic_explore_projects_path(topic_name: topic.name)
end
- it 'shows name, avatar and description as markdown' do
- expect(page).to have_content(topic.name)
+ it 'shows title, avatar and description as markdown' do
+ expect(page).to have_content(topic.title)
+ expect(page).not_to have_content(topic.name)
expect(page).to have_selector('.avatar-container > img.topic-avatar')
expect(find('.topic-description')).to have_selector('p > strong')
expect(find('.topic-description')).to have_selector('p > a[rel]')
diff --git a/spec/features/user_sorts_things_spec.rb b/spec/features/user_sorts_things_spec.rb
index fa37d692225..bcf3defe9c6 100644
--- a/spec/features/user_sorts_things_spec.rb
+++ b/spec/features/user_sorts_things_spec.rb
@@ -6,7 +6,7 @@ require "spec_helper"
# to check if the sorting option set by user is being kept persisted while going through pages.
# The `it`s are named here by convention `starting point -> some pages -> final point`.
# All those specs are moved out to this spec intentionally to keep them all in one place.
-RSpec.describe "User sorts things" do
+RSpec.describe "User sorts things", :js do
include Spec::Support::Helpers::Features::SortingHelpers
include DashboardHelper
@@ -16,29 +16,32 @@ RSpec.describe "User sorts things" do
let_it_be(:merge_request) { create(:merge_request, target_project: project, source_project: project, author: current_user) }
before do
+ stub_feature_flags(vue_issues_list: true)
+
project.add_developer(current_user)
sign_in(current_user)
end
it "issues -> project home page -> issues" do
- sort_option = 'Updated date'
+ sort_option = s_('SortOptions|Updated date')
visit(project_issues_path(project))
- sort_by(sort_option)
+ click_button s_('SortOptions|Created date')
+ click_button sort_option
visit(project_path(project))
visit(project_issues_path(project))
- expect(find(".issues-filters")).to have_content(sort_option)
+ expect(page).to have_button(sort_option)
end
it "merge requests -> dashboard merge requests" do
- sort_option = 'Updated date'
+ sort_option = s_('SortOptions|Updated date')
visit(project_merge_requests_path(project))
- sort_by(sort_option)
+ pajamas_sort_by(sort_option)
visit(assigned_mrs_dashboard_path)
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index 822bf898034..efb7c98d63a 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -167,7 +167,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
it 'does not update Devise trackable attributes' do
expect { gitlab_sign_in(user, password: user.password) }
- .not_to change { User.ghost.reload.sign_in_count }
+ .not_to change { user.reload.sign_in_count }
end
end