diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-19 07:33:21 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-19 07:33:21 +0000 |
commit | 36a59d088eca61b834191dacea009677a96c052f (patch) | |
tree | e4f33972dab5d8ef79e3944a9f403035fceea43f /spec/features | |
parent | a1761f15ec2cae7c7f7bbda39a75494add0dfd6f (diff) | |
download | gitlab-ce-36a59d088eca61b834191dacea009677a96c052f.tar.gz |
Add latest changes from gitlab-org/gitlab@15-0-stable-eev15.0.0-rc42
Diffstat (limited to 'spec/features')
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) { "'><iframe/srcdoc=''></iframe>" } 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 |