diff options
Diffstat (limited to 'spec/features')
190 files changed, 2882 insertions, 2351 deletions
diff --git a/spec/features/admin/admin_abuse_reports_spec.rb b/spec/features/admin/admin_abuse_reports_spec.rb index 192182adddc..3a02ce89aa9 100644 --- a/spec/features/admin/admin_abuse_reports_spec.rb +++ b/spec/features/admin/admin_abuse_reports_spec.rb @@ -56,7 +56,7 @@ RSpec.describe "Admin::AbuseReports", :js do describe 'filtering by user' do let!(:user2) { create(:user) } - let!(:abuse_report) { create(:abuse_report, user: user) } + let!(:abuse_report) { create(:abuse_report, user: user) } let!(:abuse_report_2) { create(:abuse_report, user: user2) } it 'shows only single user report' do diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index cd136af8d69..61e7efbc56c 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -66,7 +66,7 @@ RSpec.describe 'Admin Appearance' do context 'when system header and footer messages are not empty' do before do - appearance.update(header_message: 'Foo', footer_message: 'Bar') + appearance.update!(header_message: 'Foo', footer_message: 'Bar') end it 'shows custom system header and footer fields' do diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index bbdf2f7f4a9..e7634f4e020 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -35,6 +35,7 @@ RSpec.describe 'Admin Groups' do expect(page).to have_field('group_path') expect(page).to have_field('group_visibility_level_0') expect(page).to have_field('description') + expect(page).to have_field('group_admin_note_attributes_note') end end @@ -47,10 +48,12 @@ RSpec.describe 'Admin Groups' do path_component = 'gitlab' group_name = 'GitLab group name' group_description = 'Description of group for GitLab' + group_admin_note = 'A note about this group by an admin' fill_in 'group_path', with: path_component fill_in 'group_name', with: group_name fill_in 'group_description', with: group_description + fill_in 'group_admin_note_attributes_note', with: group_admin_note click_button "Create group" expect(current_path).to eq admin_group_path(Group.find_by(path: path_component)) @@ -61,6 +64,8 @@ RSpec.describe 'Admin Groups' do expect(li_texts).to match group_name expect(li_texts).to match path_component expect(li_texts).to match group_description + p_texts = content.all('p').collect(&:text).join('/n') + expect(p_texts).to match group_admin_note end it 'shows the visibility level radio populated with the default value' do @@ -116,6 +121,16 @@ RSpec.describe 'Admin Groups' do expect(page).to have_link(group.name, href: group_path(group)) end + + it 'has a note if one is available' do + group = create(:group, :private) + note_text = 'A group administrator note' + group.update!(admin_note_attributes: { note: note_text }) + + visit admin_group_path(group) + + expect(page).to have_text(note_text) + end end describe 'group edit' do @@ -145,6 +160,36 @@ RSpec.describe 'Admin Groups' do expect(name_field.value).to eq original_name end + + it 'adding an admin note to group without one' do + group = create(:group, :private) + expect(group.admin_note).to be_nil + + visit admin_group_edit_path(group) + admin_note_text = 'A note by an administrator' + + fill_in 'group_admin_note_attributes_note', with: admin_note_text + click_button 'Save changes' + + expect(page).to have_content(admin_note_text) + end + + it 'editing an existing group admin note' do + admin_note_text = 'A note by an administrator' + new_admin_note_text = 'A new note by an administrator' + group = create(:group, :private) + group.create_admin_note(note: admin_note_text) + + visit admin_group_edit_path(group) + + admin_note_field = find('#group_admin_note_attributes_note') + expect(admin_note_field.value).to eq(admin_note_text) + + fill_in 'group_admin_note_attributes_note', with: new_admin_note_text + click_button 'Save changes' + + expect(page).to have_content(new_admin_note_text) + end end describe 'add user into a group', :js do diff --git a/spec/features/admin/admin_labels_spec.rb b/spec/features/admin/admin_labels_spec.rb index 815a73b1450..43fb1f31a0f 100644 --- a/spec/features/admin/admin_labels_spec.rb +++ b/spec/features/admin/admin_labels_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' RSpec.describe 'admin issues labels' do - let!(:bug_label) { Label.create(title: 'bug', template: true) } - let!(:feature_label) { Label.create(title: 'feature', template: true) } + let!(:bug_label) { Label.create!(title: 'bug', template: true) } + let!(:feature_label) { Label.create!(title: 'feature', template: true) } before do admin = create(:admin) @@ -36,7 +36,7 @@ RSpec.describe 'admin issues labels' do it 'deletes all labels', :js do page.within '.labels' do - page.all('.remove-row').each do |remove| + page.all('.js-remove-row').each do |remove| accept_confirm { remove.click } wait_for_requests end diff --git a/spec/features/admin/admin_mode/login_spec.rb b/spec/features/admin/admin_mode/login_spec.rb index f1dee075925..5b2dfdb2941 100644 --- a/spec/features/admin/admin_mode/login_spec.rb +++ b/spec/features/admin/admin_mode/login_spec.rb @@ -86,7 +86,7 @@ RSpec.describe 'Admin Mode Login' do expect(codes.size).to eq 10 # Ensure the generated codes get saved - user.save + user.save! end context 'with valid code' do diff --git a/spec/features/admin/admin_mode/logout_spec.rb b/spec/features/admin/admin_mode/logout_spec.rb index b7fa59bbfb7..8cfac5d8b99 100644 --- a/spec/features/admin/admin_mode/logout_spec.rb +++ b/spec/features/admin/admin_mode/logout_spec.rb @@ -9,6 +9,8 @@ RSpec.describe 'Admin Mode Logout', :js do let(:user) { create(:admin) } before do + stub_feature_flags(combined_menu: false) + gitlab_sign_in(user) gitlab_enable_admin_mode_sign_in(user) visit admin_root_path diff --git a/spec/features/admin/admin_mode_spec.rb b/spec/features/admin/admin_mode_spec.rb index 8169b3a20db..633de20c82d 100644 --- a/spec/features/admin/admin_mode_spec.rb +++ b/spec/features/admin/admin_mode_spec.rb @@ -9,10 +9,12 @@ RSpec.describe 'Admin mode' do let(:admin) { create(:admin) } before do + stub_feature_flags(combined_menu: false) + stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') end - context 'feature flag :user_mode_in_session is enabled', :request_store do + context 'application setting :admin_mode is enabled', :request_store do before do sign_in(admin) end @@ -155,9 +157,9 @@ RSpec.describe 'Admin mode' do end end - context 'feature flag :user_mode_in_session is disabled' do + context 'application setting :admin_mode is disabled' do before do - stub_feature_flags(user_mode_in_session: false) + stub_application_setting(admin_mode: false) sign_in(admin) end diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index 4f135b81bdf..4e0dcbdf075 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -201,21 +201,21 @@ RSpec.describe "Admin Runners" do visit admin_runners_path - within '.runners-content .gl-responsive-table-row:nth-child(2)' do + within '[data-testid="runners-table"] .gl-responsive-table-row:nth-child(2)' do expect(page).to have_content 'runner-2' end - within '.runners-content .gl-responsive-table-row:nth-child(3)' do + within '[data-testid="runners-table"] .gl-responsive-table-row:nth-child(3)' do expect(page).to have_content 'runner-1' end sorting_by 'Last Contact' - within '.runners-content .gl-responsive-table-row:nth-child(2)' do + within '[data-testid="runners-table"] .gl-responsive-table-row:nth-child(2)' do expect(page).to have_content 'runner-1' end - within '.runners-content .gl-responsive-table-row:nth-child(3)' do + within '[data-testid="runners-table"] .gl-responsive-table-row:nth-child(3)' do expect(page).to have_content 'runner-2' end end @@ -285,8 +285,16 @@ RSpec.describe "Admin Runners" do end describe 'runner page breadcrumbs' do - it 'contains the current runner’s short sha' do - expect(page.find('h2')).to have_content(runner.short_sha) + it 'contains the current runner token' do + page.within '[data-testid="breadcrumb-links"]' do + expect(page.find('h2')).to have_content(runner.short_sha) + end + end + end + + describe 'runner page title', :js do + it 'contains the runner id' do + expect(find('.page-title')).to have_content("Runner ##{runner.id}") end end @@ -313,11 +321,11 @@ RSpec.describe "Admin Runners" do describe 'enable/create' do shared_examples 'assignable runner' do it 'enables a runner for a project' do - within '.unassigned-projects' do + within '[data-testid="unassigned-projects"]' do click_on 'Enable' end - assigned_project = page.find('.assigned-projects') + assigned_project = page.find('[data-testid="assigned-projects"]') expect(assigned_project).to have_content(@project2.path) end @@ -347,7 +355,7 @@ RSpec.describe "Admin Runners" do let(:runner) { create(:ci_runner, :instance) } before do - @project1.destroy + @project1.destroy! visit admin_runner_path(runner) end @@ -363,11 +371,11 @@ RSpec.describe "Admin Runners" do end it 'enables specific runner for project' do - within '.assigned-projects' do + within '[data-testid="assigned-projects"]' do click_on 'Disable' end - new_runner_project = page.find('.unassigned-projects') + new_runner_project = page.find('[data-testid="unassigned-projects"]') expect(new_runner_project).to have_content(@project1.path) end diff --git a/spec/features/admin/admin_search_settings_spec.rb b/spec/features/admin/admin_search_settings_spec.rb index a78d17a6651..cd61a1db6f3 100644 --- a/spec/features/admin/admin_search_settings_spec.rb +++ b/spec/features/admin/admin_search_settings_spec.rb @@ -20,8 +20,10 @@ RSpec.describe 'Admin searches application settings', :js do end context 'in ci/cd settings page' do - let(:visit_path) { ci_cd_admin_application_settings_path } + before do + visit(ci_cd_admin_application_settings_path) + end - it_behaves_like 'can search settings with feature flag check', 'Variables', 'Package Registry' + it_behaves_like 'can search settings', 'Variables', 'Package Registry' end end diff --git a/spec/features/admin/admin_sees_project_statistics_spec.rb b/spec/features/admin/admin_sees_project_statistics_spec.rb index be781730924..3433cc01b8e 100644 --- a/spec/features/admin/admin_sees_project_statistics_spec.rb +++ b/spec/features/admin/admin_sees_project_statistics_spec.rb @@ -21,7 +21,7 @@ RSpec.describe "Admin > Admin sees project statistics" do end context 'when project has no statistics' do - let(:project) { create(:project, :repository) { |project| project.statistics.destroy } } + let(:project) { create(:project, :repository) { |project| project.statistics.destroy! } } it "shows 'Storage: Unknown'" do expect(page).to have_content("Storage: Unknown") diff --git a/spec/features/admin/admin_sees_projects_statistics_spec.rb b/spec/features/admin/admin_sees_projects_statistics_spec.rb index 2e96814d1e9..d340eb47f34 100644 --- a/spec/features/admin/admin_sees_projects_statistics_spec.rb +++ b/spec/features/admin/admin_sees_projects_statistics_spec.rb @@ -7,7 +7,7 @@ RSpec.describe "Admin > Admin sees projects statistics" do before do create(:project, :repository) - create(:project, :repository) { |project| project.statistics.destroy } + create(:project, :repository) { |project| project.statistics.destroy! } sign_in(current_user) gitlab_enable_admin_mode_sign_in(current_user) diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 249621f5835..9a2e2eb2f6f 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -9,7 +9,7 @@ RSpec.describe 'Admin updates settings' do let(:admin) { create(:admin) } - context 'feature flag :user_mode_in_session is enabled', :request_store do + context 'application setting :admin_mode is enabled', :request_store do before do stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') sign_in(admin) @@ -129,7 +129,7 @@ RSpec.describe 'Admin updates settings' do context 'Change Sign-up restrictions' do context 'Require Admin approval for new signup setting' do - it 'changes the setting' do + it 'changes the setting', :js do page.within('.as-signup') do check 'Require admin approval for new sign-ups' click_button 'Save changes' @@ -249,6 +249,14 @@ RSpec.describe 'Admin updates settings' do expect(page).to have_content "Application settings saved successfully" expect(current_settings.hide_third_party_offers).to be true end + end + + context 'when the Slack Notifications Service template is active' do + before do + create(:service, :template, type: 'SlackService', active: true) + + visit general_admin_application_settings_path + end it 'change Slack Notifications Service template settings', :js do first(:link, 'Service Templates').click @@ -588,7 +596,7 @@ RSpec.describe 'Admin updates settings' do context 'Nav bar' do it 'shows default help links in nav' do - default_support_url = 'https://about.gitlab.com/getting-help/' + default_support_url = "https://#{ApplicationHelper.promo_host}/getting-help/" visit root_dashboard_path @@ -615,9 +623,9 @@ RSpec.describe 'Admin updates settings' do end end - context 'feature flag :user_mode_in_session is disabled' do + context 'application setting :admin_mode is disabled' do before do - stub_feature_flags(user_mode_in_session: false) + stub_application_setting(admin_mode: false) stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb index cae190e76b0..dc528dd92d4 100644 --- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb +++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb @@ -75,7 +75,7 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do end it "removes expired tokens from 'active' section" do - impersonation_token.update(expires_at: 5.days.ago) + impersonation_token.update!(expires_at: 5.days.ago) visit admin_user_impersonation_tokens_path(user_id: user.username) diff --git a/spec/features/admin/services/admin_activates_prometheus_spec.rb b/spec/features/admin/services/admin_activates_prometheus_spec.rb deleted file mode 100644 index a225de365c8..00000000000 --- a/spec/features/admin/services/admin_activates_prometheus_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Admin activates Prometheus', :js do - let(:admin) { create(:user, :admin) } - - before do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - - visit(admin_application_settings_services_path) - - click_link('Prometheus') - end - - it 'activates service' do - check('Active') - fill_in('API URL', with: 'http://prometheus.example.com') - click_button('Save changes') - - expect(page).to have_content('Application settings saved successfully') - end -end diff --git a/spec/features/admin/services/admin_visits_service_templates_spec.rb b/spec/features/admin/services/admin_visits_service_templates_spec.rb index 563bca8b32f..1fd8c8316e3 100644 --- a/spec/features/admin/services/admin_visits_service_templates_spec.rb +++ b/spec/features/admin/services/admin_visits_service_templates_spec.rb @@ -9,23 +9,45 @@ RSpec.describe 'Admin visits service templates' do before do sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) - - visit(admin_application_settings_services_path) end - context 'without instance-level integration' do - it 'shows a link to service template' do - expect(page).to have_link('Slack', href: edit_admin_application_settings_service_path(slack_service.id)) - expect(page).not_to have_link('Slack', href: edit_admin_application_settings_integration_path(slack_service)) + context 'without an active service template' do + before do + visit(admin_application_settings_services_path) + end + + it 'does not show service template content' do + expect(page).not_to have_content('Service template allows you to set default values for integrations') end end - context 'with instance-level integration' do - let_it_be(:slack_instance_integration) { create(:slack_service, instance: true, project: nil) } + context 'with an active service template' do + before do + create(:slack_service, :template, active: true) + visit(admin_application_settings_services_path) + end + + it 'shows service template content' do + expect(page).to have_content('Service template allows you to set default values for integrations') + end + + context 'without instance-level integration' do + it 'shows a link to service template' do + expect(page).to have_link('Slack', href: edit_admin_application_settings_service_path(slack_service.id)) + expect(page).not_to have_link('Slack', href: edit_admin_application_settings_integration_path(slack_service)) + end + end + + context 'with instance-level integration' do + before do + create(:slack_service, instance: true, project: nil) + visit(admin_application_settings_services_path) + end - it 'shows a link to instance-level integration' do - expect(page).not_to have_link('Slack', href: edit_admin_application_settings_service_path(slack_service.id)) - expect(page).to have_link('Slack', href: edit_admin_application_settings_integration_path(slack_service)) + it 'shows a link to instance-level integration' do + expect(page).not_to have_link('Slack', href: edit_admin_application_settings_service_path(slack_service.id)) + expect(page).to have_link('Slack', href: edit_admin_application_settings_integration_path(slack_service)) + end end end end diff --git a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb index 60f2f776595..6675abd6b42 100644 --- a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb +++ b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb @@ -25,7 +25,7 @@ RSpec.describe 'Alert integrations settings form', :js do it 'shows the alerts setting form title' do page.within('#js-alert-management-settings') do - expect(find('h4')).to have_content('Alerts') + expect(find('h4')).to have_content('Alert integrations') end end diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb deleted file mode 100644 index 8d0fa3e023b..00000000000 --- a/spec/features/boards/add_issues_modal_spec.rb +++ /dev/null @@ -1,270 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Issue Boards add issue modal', :js do - let(:project) { create(:project, :public) } - let(:board) { create(:board, project: project) } - let(:user) { create(:user) } - let!(:planning) { create(:label, project: project, name: 'Planning') } - let!(:label) { create(:label, project: project) } - let!(:list1) { create(:list, board: board, label: planning, position: 0) } - let!(:list2) { create(:list, board: board, label: label, position: 1) } - let!(:issue) { create(:issue, project: project, title: 'abc', description: 'def') } - let!(:issue2) { create(:issue, project: project, title: 'hij', description: 'klm') } - - before do - project.add_maintainer(user) - - sign_in(user) - - visit project_board_path(project, board) - wait_for_requests - end - - it 'resets filtered search state' do - visit project_board_path(project, board, search: 'testing') - - wait_for_requests - - click_button('Add issues') - - page.within('.add-issues-modal') do - expect(find('.form-control').value).to eq('') - expect(page).to have_selector('.clear-search', visible: false) - expect(find('.form-control')[:placeholder]).to eq('Search or filter results...') - end - end - - context 'modal interaction' do - before do - stub_feature_flags(add_issues_button: true) - end - - it 'opens modal' do - click_button('Add issues') - - expect(page).to have_selector('.add-issues-modal') - end - - it 'closes modal' do - click_button('Add issues') - - page.within('.add-issues-modal') do - find('.close').click - end - - expect(page).not_to have_selector('.add-issues-modal') - end - - it 'closes modal if cancel button clicked' do - click_button('Add issues') - - page.within('.add-issues-modal') do - click_button 'Cancel' - end - - expect(page).not_to have_selector('.add-issues-modal') - end - - it 'does not show tooltip on add issues button' do - button = page.find('.filter-dropdown-container button', text: 'Add issues') - - expect(button[:title]).not_to eq("Please add a list to your board first") - end - end - - context 'issues list' do - before do - stub_feature_flags(add_issues_button: true) - click_button('Add issues') - - wait_for_requests - end - - it 'loads issues' do - page.within('.add-issues-modal') do - page.within('.gl-tabs') do - expect(page).to have_content('2') - end - - expect(page).to have_selector('.board-card', count: 2) - end - end - - it 'shows selected issues tab and empty state message' do - page.within('.add-issues-modal') do - click_link 'Selected issues' - - expect(page).not_to have_selector('.board-card') - expect(page).to have_content("Go back to Open issues and select some issues to add to your board.") - end - end - - context 'list dropdown' do - it 'resets after deleting list' do - page.within('.add-issues-modal') do - expect(find('.add-issues-footer')).to have_button(planning.title) - - click_button 'Cancel' - end - - page.within(find('.board:nth-child(2)')) do - find('button[title="List settings"]').click - end - - page.within(find('.js-board-settings-sidebar')) do - accept_confirm { find('[data-testid="remove-list"]').click } - end - - click_button('Add issues') - - wait_for_requests - - page.within('.add-issues-modal') do - expect(find('.add-issues-footer')).not_to have_button(planning.title) - expect(find('.add-issues-footer')).to have_button(label.title) - end - end - end - - context 'search' do - it 'returns issues' do - page.within('.add-issues-modal') do - find('.form-control').native.send_keys(issue.title) - find('.form-control').native.send_keys(:enter) - - wait_for_requests - - expect(page).to have_selector('.board-card', count: 1) - end - end - - it 'returns no issues' do - page.within('.add-issues-modal') do - find('.form-control').native.send_keys('testing search') - find('.form-control').native.send_keys(:enter) - - wait_for_requests - - expect(page).not_to have_selector('.board-card') - expect(page).not_to have_content("You haven't added any issues to your project yet") - end - end - end - - context 'selecting issues' do - it 'selects single issue' do - page.within('.add-issues-modal') do - first('.board-card .board-card-number').click - - page.within('.gl-tabs') do - expect(page).to have_content('Selected issues 1') - end - end - end - - it 'changes button text' do - page.within('.add-issues-modal') do - first('.board-card .board-card-number').click - - expect(first('.add-issues-footer .btn')).to have_content('Add 1 issue') - end - end - - it 'changes button text with plural' do - page.within('.add-issues-modal') do - all('.board-card .js-board-card-number-container').each do |el| - el.click - end - - expect(first('.add-issues-footer .btn')).to have_content('Add 2 issues') - end - end - - it 'shows only selected issues on selected tab' do - page.within('.add-issues-modal') do - first('.board-card .board-card-number').click - - click_link 'Selected issues' - - expect(page).to have_selector('.board-card', count: 1) - end - end - - it 'selects all issues' do - page.within('.add-issues-modal') do - click_button 'Select all' - - expect(page).to have_selector('.is-active', count: 2) - end - end - - it 'deselects all issues' do - page.within('.add-issues-modal') do - click_button 'Select all' - - expect(page).to have_selector('.is-active', count: 2) - - click_button 'Deselect all' - - expect(page).not_to have_selector('.is-active') - end - end - - it "selects all that aren't already selected" do - page.within('.add-issues-modal') do - first('.board-card .board-card-number').click - - expect(page).to have_selector('.is-active', count: 1) - - click_button 'Select all' - - expect(page).to have_selector('.is-active', count: 2) - end - end - - it 'unselects from selected tab' do - page.within('.add-issues-modal') do - first('.board-card .board-card-number').click - - click_link 'Selected issues' - - first('.board-card .board-card-number').click - - expect(page).not_to have_selector('.is-active') - end - end - end - - context 'adding issues' do - it 'adds to board' do - page.within('.add-issues-modal') do - first('.board-card .board-card-number').click - - click_button 'Add 1 issue' - end - - page.within(find('.board:nth-child(2)')) do - expect(page).to have_selector('.board-card') - end - end - - it 'adds to second list' do - page.within('.add-issues-modal') do - first('.board-card .board-card-number').click - - click_button planning.title - - click_link label.title - - click_button 'Add 1 issue' - end - - page.within(find('.board:nth-child(3)')) do - expect(page).to have_selector('.board-card') - end - end - end - end -end diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index 2392f9d2f8a..ab544022bff 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Issue Boards', :js do +RSpec.describe 'Project issue boards', :js do include DragTo include MobileHelpers @@ -23,7 +23,7 @@ RSpec.describe 'Issue Boards', :js do context 'no lists' do before do - visit project_board_path(project, board) + visit_project_board_path_without_query_limit(project, board) end it 'creates default lists' do @@ -52,6 +52,7 @@ RSpec.describe 'Issue Boards', :js do let_it_be(:a_plus) { create(:label, project: project, name: 'A+') } let_it_be(:list1) { create(:list, board: board, label: planning, position: 0) } let_it_be(:list2) { create(:list, board: board, label: development, position: 1) } + let_it_be(:backlog_list) { create(:backlog_list, board: board) } let_it_be(:confidential_issue) { create(:labeled_issue, :confidential, project: project, author: user, labels: [planning], relative_position: 9) } let_it_be(:issue1) { create(:labeled_issue, project: project, title: 'aaa', description: '111', assignees: [user], labels: [planning], relative_position: 8) } @@ -68,7 +69,7 @@ RSpec.describe 'Issue Boards', :js do before do stub_feature_flags(board_new_list: false) - visit project_board_path(project, board) + visit_project_board_path_without_query_limit(project, board) wait_for_requests @@ -121,7 +122,8 @@ RSpec.describe 'Issue Boards', :js do context 'with the NOT queries feature flag disabled' do before do stub_feature_flags(not_issuable_queries: false) - visit project_board_path(project, board) + + visit_project_board_path_without_query_limit(project, board) end it 'does not have the != option' do @@ -141,7 +143,8 @@ RSpec.describe 'Issue Boards', :js do context 'with the NOT queries feature flag enabled' do before do stub_feature_flags(not_issuable_queries: true) - visit project_board_path(project, board) + + visit_project_board_path_without_query_limit(project, board) end it 'does not have the != option' do @@ -171,8 +174,7 @@ RSpec.describe 'Issue Boards', :js do it 'infinite scrolls list' do create_list(:labeled_issue, 50, project: project, labels: [planning]) - visit project_board_path(project, board) - wait_for_requests + visit_project_board_path_without_query_limit(project, board) page.within(find('.board:nth-child(2)')) do expect(page.find('.board-header')).to have_content('58') @@ -180,15 +182,19 @@ RSpec.describe 'Issue Boards', :js do expect(page).to have_content('Showing 20 of 58 issues') find('.board .board-list') - evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") - wait_for_requests + + inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do + evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") + end expect(page).to have_selector('.board-card', count: 40) expect(page).to have_content('Showing 40 of 58 issues') find('.board .board-list') - evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") - wait_for_requests + + inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do + evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") + end expect(page).to have_selector('.board-card', count: 58) expect(page).to have_content('Showing all issues') @@ -236,13 +242,13 @@ RSpec.describe 'Issue Boards', :js do wait_for_board_cards(4, 1) expect(find('.board:nth-child(2)')).to have_content(development.title) - expect(find('.board:nth-child(2)')).to have_content(planning.title) + expect(find('.board:nth-child(3)')).to have_content(planning.title) # Make sure list positions are preserved after a reload - visit project_board_path(project, board) + visit_project_board_path_without_query_limit(project, board) expect(find('.board:nth-child(2)')).to have_content(development.title) - expect(find('.board:nth-child(2)')).to have_content(planning.title) + expect(find('.board:nth-child(3)')).to have_content(planning.title) end it 'dragging does not duplicate list' do @@ -254,7 +260,8 @@ RSpec.describe 'Issue Boards', :js do expect(page).to have_selector(selector, text: development.title, count: 1) end - it 'issue moves between lists and does not show the "Development" label since the card is in the "Development" list label' do + # TODO https://gitlab.com/gitlab-org/gitlab/-/issues/323551 + xit 'issue moves between lists and does not show the "Development" label since the card is in the "Development" list label' do drag(list_from_index: 1, from_index: 1, list_to_index: 2) wait_for_board_cards(2, 7) @@ -467,14 +474,16 @@ RSpec.describe 'Issue Boards', :js do end it 'removes filtered labels' do - set_filter("label", testing.title) - click_filter_link(testing.title) - submit_filter + inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do + set_filter("label", testing.title) + click_filter_link(testing.title) + submit_filter - wait_for_board_cards(2, 1) + wait_for_board_cards(2, 1) - find('.clear-search').click - submit_filter + find('.clear-search').click + submit_filter + end wait_for_board_cards(2, 8) end @@ -484,7 +493,9 @@ RSpec.describe 'Issue Boards', :js do set_filter("label", testing.title) click_filter_link(testing.title) - submit_filter + inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do + submit_filter + end wait_for_requests @@ -494,13 +505,18 @@ RSpec.describe 'Issue Boards', :js do expect(page).to have_content('Showing 20 of 51 issues') find('.board .board-list') - evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") + + inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do + evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") + end expect(page).to have_selector('.board-card', count: 40) expect(page).to have_content('Showing 40 of 51 issues') find('.board .board-list') - evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") + inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do + evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") + end expect(page).to have_selector('.board-card', count: 51) expect(page).to have_content('Showing all issues') @@ -569,7 +585,7 @@ RSpec.describe 'Issue Boards', :js do context 'keyboard shortcuts' do before do - visit project_board_path(project, board) + visit_project_board_path_without_query_limit(project, board) wait_for_requests end @@ -617,15 +633,19 @@ RSpec.describe 'Issue Boards', :js do def drag(selector: '.board-list', list_from_index: 0, from_index: 0, to_index: 0, list_to_index: 0, perform_drop: true) # ensure there is enough horizontal space for four boards - resize_window(2000, 800) - - drag_to(selector: selector, - scrollable: '#board-app', - list_from_index: list_from_index, - from_index: from_index, - to_index: to_index, - list_to_index: list_to_index, - perform_drop: perform_drop) + inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do + resize_window(2000, 800) + + drag_to(selector: selector, + scrollable: '#board-app', + list_from_index: list_from_index, + from_index: from_index, + to_index: to_index, + list_to_index: list_to_index, + perform_drop: perform_drop) + end + + wait_for_requests end def wait_for_board_cards(board_number, expected_cards) @@ -666,4 +686,10 @@ RSpec.describe 'Issue Boards', :js do accept_confirm { find('[data-testid="remove-list"]').click } end end + + def visit_project_board_path_without_query_limit(project, board) + inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do + visit project_board_path(project, board) + end + end end diff --git a/spec/features/boards/focus_mode_spec.rb b/spec/features/boards/focus_mode_spec.rb index b1684ad69a6..2bd1e625236 100644 --- a/spec/features/boards/focus_mode_spec.rb +++ b/spec/features/boards/focus_mode_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'Issue Boards focus mode', :js do wait_for_requests end - it 'shows focus mode button to guest users' do - expect(page).to have_selector('.board-extra-actions .js-focus-mode-btn') + it 'shows focus mode button to anonymous users' do + expect(page).to have_selector('.js-focus-mode-btn') end end diff --git a/spec/features/boards/modal_filter_spec.rb b/spec/features/boards/modal_filter_spec.rb deleted file mode 100644 index 5aeb9eb5e50..00000000000 --- a/spec/features/boards/modal_filter_spec.rb +++ /dev/null @@ -1,228 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Issue Boards add issue modal filtering', :js do - let(:project) { create(:project, :public) } - let(:board) { create(:board, project: project) } - let(:planning) { create(:label, project: project, name: 'Planning') } - let!(:list1) { create(:list, board: board, label: planning, position: 0) } - let(:user) { create(:user) } - let(:user2) { create(:user) } - let!(:issue1) { create(:issue, project: project) } - - before do - project.add_maintainer(user) - - sign_in(user) - end - - it 'shows empty state when no results found' do - visit_board - - page.within('.add-issues-modal') do - find('.form-control').native.send_keys('testing empty state') - find('.form-control').native.send_keys(:enter) - - wait_for_requests - - expect(page).to have_content('There are no issues to show.') - end - end - - it 'restores filters when closing' do - visit_board - - set_filter('milestone') - click_filter_link('Upcoming') - submit_filter - - page.within('.add-issues-modal') do - wait_for_requests - - expect(page).to have_selector('.board-card', count: 0) - - click_button 'Cancel' - end - - click_button('Add issues') - - page.within('.add-issues-modal') do - wait_for_requests - - expect(page).to have_selector('.board-card', count: 1) - end - end - - it 'resotres filters after clicking clear button' do - visit_board - - set_filter('milestone') - click_filter_link('Upcoming') - submit_filter - - page.within('.add-issues-modal') do - wait_for_requests - - expect(page).to have_selector('.board-card', count: 0) - - find('.clear-search').click - - wait_for_requests - - expect(page).to have_selector('.board-card', count: 1) - end - end - - context 'author' do - let!(:issue) { create(:issue, project: project, author: user2) } - - before do - project.add_developer(user2) - - visit_board - end - - it 'filters by selected user' do - set_filter('author') - click_filter_link(user2.name) - submit_filter - - page.within('.add-issues-modal') do - wait_for_requests - - expect(page).to have_selector('.js-visual-token', text: user2.name) - expect(page).to have_selector('.board-card', count: 1) - end - end - end - - context 'assignee' do - let!(:issue) { create(:issue, project: project, assignees: [user2]) } - - before do - project.add_developer(user2) - - visit_board - end - - it 'filters by unassigned' do - set_filter('assignee') - click_filter_link('None') - submit_filter - - page.within('.add-issues-modal') do - wait_for_requests - - expect(page).to have_selector('.js-visual-token', text: 'None') - expect(page).to have_selector('.board-card', count: 1) - end - end - - it 'filters by selected user' do - set_filter('assignee') - click_filter_link(user2.name) - submit_filter - - page.within('.add-issues-modal') do - wait_for_requests - - expect(page).to have_selector('.js-visual-token', text: user2.name) - expect(page).to have_selector('.board-card', count: 1) - end - end - end - - context 'milestone' do - let(:milestone) { create(:milestone, project: project) } - let!(:issue) { create(:issue, project: project, milestone: milestone) } - - before do - visit_board - end - - it 'filters by upcoming milestone' do - set_filter('milestone') - click_filter_link('Upcoming') - submit_filter - - page.within('.add-issues-modal') do - wait_for_requests - - expect(page).to have_selector('.js-visual-token', text: 'Upcoming') - expect(page).to have_selector('.board-card', count: 0) - end - end - - it 'filters by selected milestone' do - set_filter('milestone') - click_filter_link(milestone.name) - submit_filter - - page.within('.add-issues-modal') do - wait_for_requests - - expect(page).to have_selector('.js-visual-token', text: milestone.name) - expect(page).to have_selector('.board-card', count: 1) - end - end - end - - context 'label' do - let(:label) { create(:label, project: project) } - let!(:issue) { create(:labeled_issue, project: project, labels: [label]) } - - before do - visit_board - end - - it 'filters by no label' do - set_filter('label') - click_filter_link('None') - submit_filter - - page.within('.add-issues-modal') do - wait_for_requests - - expect(page).to have_selector('.js-visual-token', text: 'None') - expect(page).to have_selector('.board-card', count: 1) - end - end - - it 'filters by label' do - set_filter('label') - click_filter_link(label.title) - submit_filter - - page.within('.add-issues-modal') do - wait_for_requests - - expect(page).to have_selector('.js-visual-token', text: label.title) - expect(page).to have_selector('.board-card', count: 1) - end - end - end - - def visit_board - visit project_board_path(project, board) - wait_for_requests - - click_button('Add issues') - end - - def set_filter(type, text = '') - find('.add-issues-modal .filtered-search').native.send_keys("#{type}:=#{text}") - end - - def submit_filter - find('.add-issues-modal .filtered-search').native.send_keys(:enter) - end - - def click_filter_link(link_text) - page.within('.add-issues-modal .filtered-search-box') do - expect(page).to have_button(link_text) - - click_button(link_text) - end - end -end diff --git a/spec/features/boards/multi_select_spec.rb b/spec/features/boards/multi_select_spec.rb index 162455f75e6..ca322355b8f 100644 --- a/spec/features/boards/multi_select_spec.rb +++ b/spec/features/boards/multi_select_spec.rb @@ -41,6 +41,10 @@ RSpec.describe 'Multi Select Issue', :js do before do project.add_maintainer(user) + # multi-drag disabled with feature flag for now + # https://gitlab.com/gitlab-org/gitlab/-/issues/289797 + stub_feature_flags(graphql_board_lists: false) + sign_in(user) end diff --git a/spec/features/boards/multiple_boards_spec.rb b/spec/features/boards/multiple_boards_spec.rb index 2894d5c7666..219f24f60d7 100644 --- a/spec/features/boards/multiple_boards_spec.rb +++ b/spec/features/boards/multiple_boards_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'Multiple Issue Boards', :js do let_it_be(:planning) { create(:label, project: project, name: 'Planning') } let_it_be(:board) { create(:board, name: 'board1', project: project) } let_it_be(:board2) { create(:board, name: 'board2', project: project) } + let(:parent) { project } let(:boards_path) { project_boards_path(project) } diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb index f434ea0c66f..20ae569322c 100644 --- a/spec/features/boards/new_issue_spec.rb +++ b/spec/features/boards/new_issue_spec.rb @@ -3,10 +3,12 @@ require 'spec_helper' RSpec.describe 'Issue Boards new issue', :js do - let(:project) { create(:project, :public) } - let(:board) { create(:board, project: project) } - let!(:list) { create(:list, board: board, position: 0) } - let(:user) { create(:user) } + let_it_be(:project) { create(:project, :public) } + let_it_be(:board) { create(:board, project: project) } + let_it_be(:backlog_list) { create(:backlog_list, board: board) } + let_it_be(:label) { create(:label, project: project, name: 'Label 1') } + let_it_be(:list) { create(:list, board: board, label: label, position: 0) } + let_it_be(:user) { create(:user) } context 'authorized user' do before do @@ -15,6 +17,7 @@ RSpec.describe 'Issue Boards new issue', :js do sign_in(user) visit project_board_path(project, board) + wait_for_requests expect(page).to have_selector('.board', count: 3) @@ -57,7 +60,7 @@ RSpec.describe 'Issue Boards new issue', :js do page.within(first('.board-new-issue-form')) do find('.form-control').set('bug') - click_button 'Submit issue' + click_button 'Create issue' end wait_for_requests @@ -70,23 +73,24 @@ RSpec.describe 'Issue Boards new issue', :js do issue = project.issues.find_by_title('bug') expect(page).to have_content(issue.to_reference) - expect(page).to have_link(issue.title, href: issue_path(issue)) + expect(page).to have_link(issue.title, href: /#{issue_path(issue)}/) end end - it 'shows sidebar when creating new issue' do + # TODO https://gitlab.com/gitlab-org/gitlab/-/issues/323446 + xit 'shows sidebar when creating new issue' do page.within(first('.board')) do find('.issue-count-badge-add-button').click end page.within(first('.board-new-issue-form')) do find('.form-control').set('bug') - click_button 'Submit issue' + click_button 'Create issue' end wait_for_requests - expect(page).to have_selector('.issue-boards-sidebar') + expect(page).to have_selector('[data-testid="issue-boards-sidebar"]') end it 'successfuly loads labels to be added to newly created issue' do @@ -96,17 +100,21 @@ RSpec.describe 'Issue Boards new issue', :js do page.within(first('.board-new-issue-form')) do find('.form-control').set('new issue') - click_button 'Submit issue' + click_button 'Create issue' end wait_for_requests - page.within(first('.issue-boards-sidebar')) do - find('.labels .edit-link').click + page.within(first('.board')) do + find('.board-card').click + end + + page.within(first('[data-testid="issue-boards-sidebar"]')) do + find('.labels [data-testid="edit-button"]').click wait_for_requests - expect(page).to have_selector('.labels .dropdown-content li a') + expect(page).to have_selector('.labels-select-contents-list .dropdown-content li a') end end end diff --git a/spec/features/boards/reload_boards_on_browser_back_spec.rb b/spec/features/boards/reload_boards_on_browser_back_spec.rb index 181cbcc9811..36682036d48 100644 --- a/spec/features/boards/reload_boards_on_browser_back_spec.rb +++ b/spec/features/boards/reload_boards_on_browser_back_spec.rb @@ -27,7 +27,7 @@ RSpec.describe 'Ensure Boards do not show stale data on browser back', :js do fill_in 'issue_title', with: 'issue should be shown' - click_button 'Submit issue' + click_button 'Create issue' page.go_back wait_for_requests @@ -43,7 +43,7 @@ RSpec.describe 'Ensure Boards do not show stale data on browser back', :js do issue = project.issues.find_by_title('issue should be shown') expect(page).to have_content(issue.to_reference) - expect(page).to have_link(issue.title, href: issue_path(issue)) + expect(page).to have_link(issue.title, href: /#{issue_path(issue)}/) end end end diff --git a/spec/features/boards/sidebar_assignee_spec.rb b/spec/features/boards/sidebar_assignee_spec.rb new file mode 100644 index 00000000000..e938612163f --- /dev/null +++ b/spec/features/boards/sidebar_assignee_spec.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Project issue boards sidebar assignee', :js do + include BoardHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :public) } + let_it_be(:development) { create(:label, project: project, name: 'Development') } + let_it_be(:regression) { create(:label, project: project, name: 'Regression') } + let_it_be(:stretch) { create(:label, project: project, name: 'Stretch') } + + let!(:issue1) { create(:labeled_issue, project: project, assignees: [user], labels: [development], relative_position: 2) } + let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) } + let(:board) { create(:board, project: project) } + let!(:list) { create(:list, board: board, label: development, position: 0) } + let(:card) { find('.board:nth-child(2)').first('.board-card') } + + before do + project.add_maintainer(user) + + sign_in(user) + + visit project_board_path(project, board) + wait_for_requests + end + + context 'assignee' do + it 'updates the issues assignee' do + click_card(card) + + page.within('.assignee') do + click_button('Edit') + + wait_for_requests + + assignee = first('.gl-avatar-labeled').find('.gl-avatar-labeled-label').text + + page.within('.dropdown-menu-user') do + first('.gl-avatar-labeled').click + end + + click_button('Apply') + wait_for_requests + + expect(page).to have_content(assignee) + end + + expect(card).to have_selector('.avatar') + end + + it 'removes the assignee' do + card_two = find('.board:nth-child(2)').find('.board-card:nth-child(2)') + click_card(card_two) + + page.within('.assignee') do + click_button('Edit') + + wait_for_requests + + page.within('.dropdown-menu-user') do + find('[data-testid="unassign"]').click + end + + click_button('Apply') + wait_for_requests + + expect(page).to have_content('None') + end + + expect(card_two).not_to have_selector('.avatar') + end + + it 'assignees to current user' do + click_card(card) + + page.within(find('.assignee')) do + expect(page).to have_content('None') + + click_button 'assign yourself' + + wait_for_requests + + expect(page).to have_content(user.name) + end + + expect(card).to have_selector('.avatar') + end + + it 'updates assignee dropdown' do + click_card(card) + + page.within('.assignee') do + click_button('Edit') + + wait_for_requests + + assignee = first('.gl-avatar-labeled').find('.gl-avatar-labeled-label').text + + page.within('.dropdown-menu-user') do + first('.gl-avatar-labeled').click + end + + click_button('Apply') + wait_for_requests + + expect(page).to have_content(assignee) + end + + page.within(find('.board:nth-child(2)')) do + find('.board-card:nth-child(2)').click + end + + page.within('.assignee') do + click_button('Edit') + + expect(find('.dropdown-menu')).to have_selector('.gl-new-dropdown-item-check-icon') + end + end + end +end diff --git a/spec/features/boards/sidebar_due_date_spec.rb b/spec/features/boards/sidebar_due_date_spec.rb new file mode 100644 index 00000000000..141c574ffec --- /dev/null +++ b/spec/features/boards/sidebar_due_date_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Project issue boards sidebar due date', :js do + include BoardHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :public) } + let_it_be(:issue) { create(:issue, project: project, relative_position: 1) } + let_it_be(:board) { create(:board, project: project) } + let_it_be(:list) { create(:list, board: board, position: 0) } + + let(:card) { find('.board:nth-child(1)').first('.board-card') } + + around do |example| + freeze_time { example.run } + end + + before do + project.add_maintainer(user) + + sign_in(user) + + visit project_board_path(project, board) + wait_for_requests + end + + context 'due date' do + it 'updates due date' do + click_card(card) + + page.within('[data-testid="sidebar-due-date"]') do + today = Date.today.day + + click_button 'Edit' + + click_button today.to_s + + wait_for_requests + + expect(page).to have_content(today.to_s(:medium)) + end + end + end +end diff --git a/spec/features/boards/sidebar_labels_spec.rb b/spec/features/boards/sidebar_labels_spec.rb new file mode 100644 index 00000000000..2f0230c61d8 --- /dev/null +++ b/spec/features/boards/sidebar_labels_spec.rb @@ -0,0 +1,166 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Project issue boards sidebar labels', :js do + include BoardHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :public) } + let_it_be(:development) { create(:label, project: project, name: 'Development') } + let_it_be(:bug) { create(:label, project: project, name: 'Bug') } + let_it_be(:regression) { create(:label, project: project, name: 'Regression') } + let_it_be(:stretch) { create(:label, project: project, name: 'Stretch') } + let_it_be(:issue1) { create(:labeled_issue, project: project, labels: [development], relative_position: 2) } + let_it_be(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) } + let_it_be(:board) { create(:board, project: project) } + let_it_be(:list) { create(:list, board: board, label: development, position: 0) } + + let(:card) { find('.board:nth-child(2)').first('.board-card') } + + before do + project.add_maintainer(user) + + sign_in(user) + + visit project_board_path(project, board) + wait_for_requests + end + + context 'labels' do + # https://gitlab.com/gitlab-org/gitlab/-/issues/322725 + xit 'shows current labels when editing' do + click_card(card) + + page.within('.labels') do + click_link 'Edit' + + wait_for_requests + + page.within('.value') do + expect(page).to have_selector('.gl-label-text', count: 2) + expect(page).to have_content(development.title) + expect(page).to have_content(stretch.title) + end + end + end + + it 'adds a single label' do + click_card(card) + + page.within('.labels') do + click_button 'Edit' + + wait_for_requests + + click_link bug.title + + find('[data-testid="close-icon"]').click + + wait_for_requests + + page.within('.value') do + expect(page).to have_selector('.gl-label-text', count: 3) + expect(page).to have_content(bug.title) + end + end + + # 'Development' label does not show since the card is in a 'Development' list label + expect(card).to have_selector('.gl-label', count: 2) + expect(card).to have_content(bug.title) + end + + it 'adds a multiple labels' do + click_card(card) + + page.within('.labels') do + click_button 'Edit' + + wait_for_requests + + click_link bug.title + + click_link regression.title + + find('[data-testid="close-icon"]').click + + wait_for_requests + + page.within('.value') do + expect(page).to have_selector('.gl-label-text', count: 4) + expect(page).to have_content(bug.title) + expect(page).to have_content(regression.title) + end + end + + # 'Development' label does not show since the card is in a 'Development' list label + expect(card).to have_selector('.gl-label', count: 3) + expect(card).to have_content(bug.title) + expect(card).to have_content(regression.title) + end + + it 'removes a label' do + click_card(card) + + page.within('.labels') do + click_button 'Edit' + + wait_for_requests + + click_link stretch.title + + find('[data-testid="close-icon"]').click + + wait_for_requests + + page.within('.value') do + expect(page).to have_selector('.gl-label-text', count: 1) + expect(page).not_to have_content(stretch.title) + end + end + + # 'Development' label does not show since the card is in a 'Development' list label + expect(card).to have_selector('.gl-label-text', count: 0) + expect(card).not_to have_content(stretch.title) + end + + # https://gitlab.com/gitlab-org/gitlab/-/issues/324290 + xit 'creates project label' do + click_card(card) + + page.within('.labels') do + click_link 'Edit' + wait_for_requests + + click_link 'Create project label' + fill_in 'new_label_name', with: 'test label' + first('.suggest-colors-dropdown a').click + click_button 'Create' + wait_for_requests + + expect(page).to have_link 'test label' + end + expect(page).to have_selector('.board', count: 3) + end + + # https://gitlab.com/gitlab-org/gitlab/-/issues/324290 + xit 'creates project label and list' do + click_card(card) + + page.within('.labels') do + click_link 'Edit' + wait_for_requests + + click_link 'Create project label' + fill_in 'new_label_name', with: 'test label' + first('.suggest-colors-dropdown a').click + first('.js-add-list').click + click_button 'Create' + wait_for_requests + + expect(page).to have_link 'test label' + end + expect(page).to have_selector('.board', count: 4) + end + end +end diff --git a/spec/features/boards/sidebar_milestones_spec.rb b/spec/features/boards/sidebar_milestones_spec.rb new file mode 100644 index 00000000000..54182781a30 --- /dev/null +++ b/spec/features/boards/sidebar_milestones_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Project issue boards sidebar milestones', :js do + include BoardHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :public) } + let_it_be(:milestone) { create(:milestone, project: project) } + let_it_be(:issue1) { create(:issue, project: project, relative_position: 1) } + let_it_be(:issue2) { create(:issue, project: project, milestone: milestone, relative_position: 2) } + let_it_be(:board) { create(:board, project: project) } + let_it_be(:list) { create(:list, board: board, position: 0) } + + let(:card1) { find('.board:nth-child(1) .board-card:nth-of-type(1)') } + let(:card2) { find('.board:nth-child(1) .board-card:nth-of-type(2)') } + + before do + project.add_maintainer(user) + + sign_in(user) + + visit project_board_path(project, board) + wait_for_requests + end + + context 'milestone' do + it 'adds a milestone' do + click_card(card1) + + page.within('[data-testid="sidebar-milestones"]') do + click_button 'Edit' + + wait_for_requests + + click_button milestone.title + + wait_for_requests + + page.within('.value') do + expect(page).to have_content(milestone.title) + end + end + end + + it 'removes a milestone' do + click_card(card2) + + page.within('[data-testid="sidebar-milestones"]') do + click_button 'Edit' + + wait_for_requests + + click_button "No milestone" + + wait_for_requests + + page.within('.value') do + expect(page).not_to have_content(milestone.title) + end + end + end + end +end diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index c79bf2abff1..977147c3c6b 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -2,412 +2,33 @@ require 'spec_helper' -RSpec.describe 'Issue Boards', :js do +RSpec.describe 'Project issue boards sidebar', :js do include BoardHelpers - include FilteredSearchHelpers - let(:user) { create(:user) } - let(:user2) { create(:user) } - let(:project) { create(:project, :public) } - let!(:milestone) { create(:milestone, project: project) } - let!(:development) { create(:label, project: project, name: 'Development') } - let!(:bug) { create(:label, project: project, name: 'Bug') } - let!(:regression) { create(:label, project: project, name: 'Regression') } - let!(:stretch) { create(:label, project: project, name: 'Stretch') } - let!(:issue1) { create(:labeled_issue, project: project, assignees: [user], milestone: milestone, labels: [development], relative_position: 2) } - let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) } - let(:board) { create(:board, project: project) } - let!(:list) { create(:list, board: board, label: development, position: 0) } - let(:card) { find('.board:nth-child(2)').first('.board-card') } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :public) } + let_it_be(:board) { create(:board, project: project) } + let_it_be(:list) { create(:list, board: board, position: 0) } - let(:application_settings) { {} } - - around do |example| - freeze_time { example.run } - end + let_it_be(:issue, reload: true) { create(:issue, project: project, relative_position: 1) } before do project.add_maintainer(user) sign_in(user) - stub_application_setting(application_settings) - visit project_board_path(project, board) - wait_for_requests - end - - it 'shows sidebar when clicking issue' do - click_card(card) - - expect(page).to have_selector('.issue-boards-sidebar') - end - - it 'closes sidebar when clicking issue' do - click_card(card) - - expect(page).to have_selector('.issue-boards-sidebar') - - click_card(card) - - expect(page).not_to have_selector('.issue-boards-sidebar') - end - - it 'closes sidebar when clicking close button' do - click_card(card) - - expect(page).to have_selector('.issue-boards-sidebar') - - find('.gutter-toggle').click - - expect(page).not_to have_selector('.issue-boards-sidebar') - end - - it 'shows issue details when sidebar is open' do - click_card(card) - - page.within('.issue-boards-sidebar') do - expect(page).to have_content(issue2.title) - expect(page).to have_content(issue2.to_reference) - end - end - - context 'assignee' do - it 'updates the issues assignee' do - click_card(card) - - page.within('.assignee') do - click_button('Edit') - - wait_for_requests - - assignee = first('.gl-avatar-labeled').find('.gl-avatar-labeled-label').text - - page.within('.dropdown-menu-user') do - first('.gl-avatar-labeled').click - end - - click_button('Edit') - wait_for_requests - - expect(page).to have_content(assignee) - end - - expect(card).to have_selector('.avatar') - end - - it 'removes the assignee' do - card_two = find('.board:nth-child(2)').find('.board-card:nth-child(2)') - click_card(card_two) - - page.within('.assignee') do - click_button('Edit') - - wait_for_requests - - page.within('.dropdown-menu-user') do - find('[data-testid="unassign"]').click - end - - click_button('Edit') - wait_for_requests - - expect(page).to have_content('None') - end - - expect(card_two).not_to have_selector('.avatar') - end - - it 'assignees to current user' do - click_card(card) - - page.within(find('.assignee')) do - expect(page).to have_content('None') - - click_button 'assign yourself' - - wait_for_requests - - expect(page).to have_content(user.name) - end - - expect(card).to have_selector('.avatar') - end - - it 'updates assignee dropdown' do - click_card(card) - - page.within('.assignee') do - click_button('Edit') - - wait_for_requests - - assignee = first('.gl-avatar-labeled').find('.gl-avatar-labeled-label').text - - page.within('.dropdown-menu-user') do - first('.gl-avatar-labeled').click - end - - click_button('Edit') - wait_for_requests - - expect(page).to have_content(assignee) - end - - page.within(find('.board:nth-child(2)')) do - find('.board-card:nth-child(2)').click - end - - page.within('.assignee') do - click_button('Edit') - - expect(find('.dropdown-menu')).to have_selector('.gl-new-dropdown-item-check-icon') - end - end - end - - context 'milestone' do - it 'adds a milestone' do - click_card(card) - - page.within('.milestone') do - click_link 'Edit' - - wait_for_requests - - click_link milestone.title - - wait_for_requests - - page.within('.value') do - expect(page).to have_content(milestone.title) - end - end - end - - it 'removes a milestone' do - click_card(card) - page.within('.milestone') do - click_link 'Edit' - - wait_for_requests - - click_link "No milestone" - - wait_for_requests - - page.within('.value') do - expect(page).not_to have_content(milestone.title) - end - end - end - end - - context 'time tracking' do - let(:compare_meter_tooltip) { find('.time-tracking .time-tracking-content .compare-meter')['title'] } - - before do - issue2.timelogs.create(time_spent: 14400, user: user) - issue2.update!(time_estimate: 128800) - - click_card(card) - end - - it 'shows time tracking progress bar' do - expect(compare_meter_tooltip).to eq('Time remaining: 3d 7h 46m') - end - - context 'when time_tracking_limit_to_hours is true' do - let(:application_settings) { { time_tracking_limit_to_hours: true } } - - it 'shows time tracking progress bar' do - expect(compare_meter_tooltip).to eq('Time remaining: 31h 46m') - end - end - end - - context 'due date' do - it 'updates due date' do - click_card(card) - - page.within('.due_date') do - click_link 'Edit' - - click_button Date.today.day - - wait_for_requests - - expect(page).to have_content(Date.today.to_s(:medium)) - end - end + wait_for_requests end - context 'labels' do - it 'shows current labels when editing' do - click_card(card) - - page.within('.labels') do - click_link 'Edit' - - wait_for_requests - - page.within('.value') do - expect(page).to have_selector('.gl-label-text', count: 2) - expect(page).to have_content(development.title) - expect(page).to have_content(stretch.title) - end - end - end - - it 'adds a single label' do - click_card(card) - - page.within('.labels') do - click_link 'Edit' - - wait_for_requests - - click_link bug.title - - wait_for_requests - - find('.dropdown-menu-close-icon').click - - page.within('.value') do - expect(page).to have_selector('.gl-label-text', count: 3) - expect(page).to have_content(bug.title) - end - end - - # 'Development' label does not show since the card is in a 'Development' list label - expect(card).to have_selector('.gl-label', count: 2) - expect(card).to have_content(bug.title) - end - - it 'adds a multiple labels' do - click_card(card) + it_behaves_like 'issue boards sidebar' - page.within('.labels') do - click_link 'Edit' - - wait_for_requests - - click_link bug.title - - wait_for_requests - - click_link regression.title - - wait_for_requests - - find('.dropdown-menu-close-icon').click - - page.within('.value') do - expect(page).to have_selector('.gl-label-text', count: 4) - expect(page).to have_content(bug.title) - expect(page).to have_content(regression.title) - end - end - - # 'Development' label does not show since the card is in a 'Development' list label - expect(card).to have_selector('.gl-label', count: 3) - expect(card).to have_content(bug.title) - expect(card).to have_content(regression.title) - end - - it 'removes a label' do - click_card(card) - - page.within('.labels') do - click_link 'Edit' - - wait_for_requests - - within('.dropdown-menu-labels') do - click_link stretch.title - end - - wait_for_requests - - find('.dropdown-menu-close-icon').click - - page.within('.value') do - expect(page).to have_selector('.gl-label-text', count: 1) - expect(page).not_to have_content(stretch.title) - end - end - - # 'Development' label does not show since the card is in a 'Development' list label - expect(card).to have_selector('.gl-label-text', count: 0) - expect(card).not_to have_content(stretch.title) - end - - it 'creates project label' do - click_card(card) - - page.within('.labels') do - click_link 'Edit' - wait_for_requests - - click_link 'Create project label' - fill_in 'new_label_name', with: 'test label' - first('.suggest-colors-dropdown a').click - click_button 'Create' - wait_for_requests - - expect(page).to have_link 'test label' - end - expect(page).to have_selector('.board', count: 3) - end - - it 'creates project label and list' do - click_card(card) - - page.within('.labels') do - click_link 'Edit' - wait_for_requests - - click_link 'Create project label' - fill_in 'new_label_name', with: 'test label' - first('.suggest-colors-dropdown a').click - first('.js-add-list').click - click_button 'Create' - wait_for_requests - - expect(page).to have_link 'test label' - end - expect(page).to have_selector('.board', count: 4) - end + def first_card + find('.board:nth-child(1)').first("[data-testid='board_card']") end - context 'subscription' do - it 'changes issue subscription' do - click_card(card) - wait_for_requests - - page.within('.subscriptions') do - find('[data-testid="subscription-toggle"] button:not(.is-checked)').click - wait_for_requests - - expect(page).to have_css('[data-testid="subscription-toggle"] button.is-checked') - end - end - - it 'has checked subscription toggle when already subscribed' do - create(:subscription, user: user, project: project, subscribable: issue2, subscribed: true) - visit project_board_path(project, board) - wait_for_requests - - click_card(card) - wait_for_requests - - page.within('.subscriptions') do - find('[data-testid="subscription-toggle"] button.is-checked').click - wait_for_requests - - expect(page).to have_css('[data-testid="subscription-toggle"] button:not(.is-checked)') - end - end + def click_first_issue_card + click_card(first_card) end end diff --git a/spec/features/boards/sub_group_project_spec.rb b/spec/features/boards/sub_group_project_spec.rb index cd3d61726f6..bde5f061a67 100644 --- a/spec/features/boards/sub_group_project_spec.rb +++ b/spec/features/boards/sub_group_project_spec.rb @@ -21,7 +21,8 @@ RSpec.describe 'Sub-group project issue boards', :js do wait_for_requests end - it 'creates new label from sidebar' do + # TODO https://gitlab.com/gitlab-org/gitlab/-/issues/324290 + xit 'creates new label from sidebar' do find('.board-card').click page.within '.labels' do diff --git a/spec/features/boards/user_adds_lists_to_board_spec.rb b/spec/features/boards/user_adds_lists_to_board_spec.rb index b9945207bb2..5128fc4004e 100644 --- a/spec/features/boards/user_adds_lists_to_board_spec.rb +++ b/spec/features/boards/user_adds_lists_to_board_spec.rb @@ -71,10 +71,13 @@ RSpec.describe 'User adds lists', :js do def select_label(board_new_list_enabled, label) if board_new_list_enabled - page.within('.board-add-new-list') do - find('label', text: label.title).click - click_button 'Add' - end + click_button 'Select a label' + + find('label', text: label.title).click + + click_button 'Add to board' + + wait_for_all_requests else page.within('.dropdown-menu-issues-board-new') do click_link label.title diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb index ee156bdcab4..0b73226268d 100644 --- a/spec/features/calendar_spec.rb +++ b/spec/features/calendar_spec.rb @@ -42,7 +42,7 @@ RSpec.describe 'Contributions Calendar', :js do "#{contributions} #{'contribution'.pluralize(contributions)}" end - "#{get_cell_color_selector(contributions)}[title='#{contribution_text}<br />#{date}']" + "#{get_cell_color_selector(contributions)}[title='#{contribution_text}<br /><span class=\"gl-text-gray-300\">#{date}</span>']" end def push_code_contribution @@ -64,7 +64,7 @@ RSpec.describe 'Contributions Calendar', :js do author_id: user.id } - Event.create(note_comment_params) + Event.create!(note_comment_params) end def selected_day_activities(visible: true) diff --git a/spec/features/callouts/service_templates_deprecation_spec.rb b/spec/features/callouts/service_templates_deprecation_spec.rb new file mode 100644 index 00000000000..b6403b54e29 --- /dev/null +++ b/spec/features/callouts/service_templates_deprecation_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Service templates deprecation callout' do + let_it_be(:admin) { create(:admin) } + let_it_be(:non_admin) { create(:user) } + let_it_be(:callout_content) { 'Service templates are deprecated and will be removed in GitLab 14.0.' } + + context 'when a non-admin is logged in' do + before do + sign_in(non_admin) + visit root_dashboard_path + end + + it 'does not display callout' do + expect(page).not_to have_content callout_content + end + end + + context 'when an admin is logged in' do + before do + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + + visit root_dashboard_path + end + + context 'with no active service templates' do + it 'does not display callout' do + expect(page).not_to have_content callout_content + end + end + + context 'with active service template' do + before do + create(:service, :template, type: 'MattermostService', active: true) + visit root_dashboard_path + end + + it 'displays callout' do + expect(page).to have_content callout_content + expect(page).to have_link 'See affected service templates', href: admin_application_settings_services_path + end + + context 'when callout is dismissed', :js do + before do + find('[data-testid="close-service-templates-deprecated-callout"]').click + + visit root_dashboard_path + end + + it 'does not display callout' do + expect(page).not_to have_content callout_content + end + end + end + end +end diff --git a/spec/features/clusters/cluster_detail_page_spec.rb b/spec/features/clusters/cluster_detail_page_spec.rb index 6fe6c099d80..84a18a45d35 100644 --- a/spec/features/clusters/cluster_detail_page_spec.rb +++ b/spec/features/clusters/cluster_detail_page_spec.rb @@ -23,7 +23,7 @@ RSpec.describe 'Clusterable > Show page' do it 'allow the user to set domain', :js do visit cluster_path - within '.js-cluster-integration-form' do + within '.js-cluster-details-form' do fill_in('cluster_base_domain', with: 'test.com') click_on 'Save changes' end @@ -39,7 +39,7 @@ RSpec.describe 'Clusterable > Show page' do end it 'shows help text with the domain as an alternative to custom domain', :js do - within '.js-cluster-integration-form' do + within '.js-cluster-details-form' do expect(find(cluster_ingress_help_text_selector).text).to include('192.168.1.100') end end @@ -49,7 +49,7 @@ RSpec.describe 'Clusterable > Show page' do it 'alternative to custom domain is not shown' do visit cluster_path - within '.js-cluster-integration-form' do + within '.js-cluster-details-form' do expect(page).not_to have_selector(cluster_ingress_help_text_selector) end end diff --git a/spec/features/clusters/cluster_health_dashboard_spec.rb b/spec/features/clusters/cluster_health_dashboard_spec.rb index e9e3b48e9c0..862f34768c4 100644 --- a/spec/features/clusters/cluster_health_dashboard_spec.rb +++ b/spec/features/clusters/cluster_health_dashboard_spec.rb @@ -33,7 +33,7 @@ RSpec.describe 'Cluster Health board', :js, :kubeclient, :use_clean_rails_memory click_link 'Health' - expect(page).to have_text('you must first install Prometheus in the Applications tab') + expect(page).to have_text('you must first enable Prometheus in the Integrations tab') end end diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 1622979812d..2dafaedd262 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -140,7 +140,7 @@ RSpec.describe 'Commits' do context 'when accessing internal project with disallowed access', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/299575' do before do - project.update( + project.update!( visibility_level: Gitlab::VisibilityLevel::INTERNAL, public_builds: false) create(:ci_job_artifact, :archive, file: artifacts_file, job: build) diff --git a/spec/features/dashboard/active_tab_spec.rb b/spec/features/dashboard/active_tab_spec.rb index ee85c136190..3a532cb4161 100644 --- a/spec/features/dashboard/active_tab_spec.rb +++ b/spec/features/dashboard/active_tab_spec.rb @@ -4,6 +4,8 @@ require 'spec_helper' RSpec.describe 'Dashboard Active Tab', :js do before do + stub_feature_flags(combined_menu: false) + sign_in(create(:user)) end diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb index c14a6001a3e..442b8904974 100644 --- a/spec/features/dashboard/datetime_on_tooltips_spec.rb +++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb @@ -13,7 +13,7 @@ RSpec.describe 'Tooltips on .timeago dates', :js do context 'on the activity tab' do before do - Event.create( project: project, author_id: user.id, action: :joined, + Event.create!( project: project, author_id: user.id, action: :joined, updated_at: created_date, created_at: created_date) sign_in user diff --git a/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb b/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb index c2a3b90b6f4..179d9d09905 100644 --- a/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb +++ b/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb @@ -8,6 +8,8 @@ RSpec.describe 'The group dashboard' do let(:user) { create(:user) } before do + stub_feature_flags(combined_menu: false) + sign_in user end diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb index 3cb7140d253..d4c6b6faa79 100644 --- a/spec/features/dashboard/issuables_counter_spec.rb +++ b/spec/features/dashboard/issuables_counter_spec.rb @@ -10,7 +10,7 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d before do issue.assignees = [user] - merge_request.update(assignees: [user]) + merge_request.update!(assignees: [user]) sign_in(user) end @@ -35,7 +35,7 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d expect_counters('merge_requests', '1') - merge_request.update(assignees: []) + merge_request.update!(assignees: []) user.invalidate_cache_counts diff --git a/spec/features/dashboard/milestones_spec.rb b/spec/features/dashboard/milestones_spec.rb index 308432b7a1b..992ed2f2ce6 100644 --- a/spec/features/dashboard/milestones_spec.rb +++ b/spec/features/dashboard/milestones_spec.rb @@ -30,7 +30,7 @@ RSpec.describe 'Dashboard > Milestones' do expect(current_path).to eq dashboard_milestones_path expect(page).to have_content(milestone.title) expect(page).to have_content(group.name) - expect(first('.milestone')).to have_content('Merge Requests') + expect(first('.milestone')).to have_content('Merge requests') end describe 'new milestones dropdown', :js do diff --git a/spec/features/dashboard/project_member_activity_index_spec.rb b/spec/features/dashboard/project_member_activity_index_spec.rb index 6e6e466294f..c26a1a0b486 100644 --- a/spec/features/dashboard/project_member_activity_index_spec.rb +++ b/spec/features/dashboard/project_member_activity_index_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'Project member activity', :js do end def visit_activities_and_wait_with_event(event_type) - Event.create(project: project, author_id: user.id, action: event_type) + Event.create!(project: project, author_id: user.id, action: event_type) visit activity_project_path(project) wait_for_requests end diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index d7330b5267b..20c753b1cdb 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -3,9 +3,9 @@ require 'spec_helper' RSpec.describe 'Dashboard Projects' do - let(:user) { create(:user) } - let(:project) { create(:project, :repository, name: 'awesome stuff') } - let(:project2) { create(:project, :public, name: 'Community project') } + let_it_be(:user) { create(:user) } + let_it_be(:project, reload: true) { create(:project, :repository) } + let_it_be(:project2) { create(:project, :public) } before do project.add_developer(user) @@ -18,17 +18,10 @@ RSpec.describe 'Dashboard Projects' do end end - it 'shows the project the user in a member of in the list' do - visit dashboard_projects_path - expect(page).to have_content('awesome stuff') - end - - it 'shows "New project" button' do + it 'shows the customize banner', :js do visit dashboard_projects_path - page.within '#content-body' do - expect(page).to have_link('New project') - end + expect(page).to have_content('Do you want to customize this page?') end context 'when user has access to the project' do @@ -48,7 +41,7 @@ RSpec.describe 'Dashboard Projects' do expect(page).to have_content('Developer') end - project.members.last.update(access_level: 40) + project.members.last.update!(access_level: 40) visit dashboard_projects_path @@ -153,7 +146,7 @@ RSpec.describe 'Dashboard Projects' do end describe 'with a pipeline', :clean_gitlab_redis_shared_state do - let(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit.sha, ref: project.default_branch) } + let_it_be(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit.sha, ref: project.default_branch) } before do # Since the cache isn't updated when a new pipeline is created @@ -190,7 +183,7 @@ RSpec.describe 'Dashboard Projects' do let(:guest_user) { create(:user) } before do - project.update(public_builds: false) + project.update!(public_builds: false) project.add_guest(guest_user) sign_in(guest_user) end diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb index b2fda28f0ec..e96a60b2ab2 100644 --- a/spec/features/dashboard/shortcuts_spec.rb +++ b/spec/features/dashboard/shortcuts_spec.rb @@ -3,6 +3,10 @@ require 'spec_helper' RSpec.describe 'Dashboard shortcuts', :js do + before do + stub_feature_flags(combined_menu: false) + end + context 'logged in' do let(:user) { create(:user) } let(:project) { create(:project) } @@ -20,7 +24,7 @@ RSpec.describe 'Dashboard shortcuts', :js do find('body').send_keys([:shift, 'M']) - check_page_title('Merge Requests') + check_page_title('Merge requests') find('body').send_keys([:shift, 'T']) diff --git a/spec/features/discussion_comments/merge_request_spec.rb b/spec/features/discussion_comments/merge_request_spec.rb index f60d7da6a30..a90ff3721d3 100644 --- a/spec/features/discussion_comments/merge_request_spec.rb +++ b/spec/features/discussion_comments/merge_request_spec.rb @@ -8,8 +8,6 @@ RSpec.describe 'Thread Comments Merge Request', :js do let(:merge_request) { create(:merge_request, source_project: project) } before do - stub_feature_flags(remove_resolve_note: false) - project.add_maintainer(user) sign_in(user) diff --git a/spec/features/error_pages_spec.rb b/spec/features/error_pages_spec.rb index 77f8aa87237..8dc9e5ade46 100644 --- a/spec/features/error_pages_spec.rb +++ b/spec/features/error_pages_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Error Pages' do +RSpec.describe 'Error Pages', :js do let(:user) { create(:user) } let(:project) { create(:project, :public) } @@ -14,7 +14,12 @@ RSpec.describe 'Error Pages' do it 'shows nav links' do expect(page).to have_link("Home", href: root_path) expect(page).to have_link("Help", href: help_path) - expect(page).to have_link(nil, href: destroy_user_session_path) + end + + it 'allows user to sign out' do + click_link 'Sign out and sign in with a different account' + + expect(page).to have_current_path(new_user_session_path) end end diff --git a/spec/features/error_tracking/user_filters_errors_by_status_spec.rb b/spec/features/error_tracking/user_filters_errors_by_status_spec.rb index a0d93b791d9..6846d8f6ade 100644 --- a/spec/features/error_tracking/user_filters_errors_by_status_spec.rb +++ b/spec/features/error_tracking/user_filters_errors_by_status_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'When a user filters Sentry errors by status', :js, :use_clean_ra let_it_be(:issues_response_body) { fixture_file('sentry/issues_sample_response.json') } let_it_be(:filtered_errors_by_status_response) { Gitlab::Json.parse(issues_response_body).filter { |error| error['status'] == 'ignored' }.to_json } + let(:issues_api_url) { "#{sentry_api_urls.issues_url}?limit=20&query=is:unresolved" } let(:issues_api_url_filter) { "#{sentry_api_urls.issues_url}?limit=20&query=is:ignored" } let(:auth_token) {{ 'Authorization' => 'Bearer access_token_123' }} diff --git a/spec/features/error_tracking/user_searches_sentry_errors_spec.rb b/spec/features/error_tracking/user_searches_sentry_errors_spec.rb index 025a6261957..c16c9d3fb1f 100644 --- a/spec/features/error_tracking/user_searches_sentry_errors_spec.rb +++ b/spec/features/error_tracking/user_searches_sentry_errors_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'When a user searches for Sentry errors', :js, :use_clean_rails_m let_it_be(:issues_response_body) { fixture_file('sentry/issues_sample_response.json') } let_it_be(:error_search_response_body) { fixture_file('sentry/error_list_search_response.json') } + let(:issues_api_url) { "#{sentry_api_urls.issues_url}?limit=20&query=is:unresolved" } let(:issues_api_url_search) { "#{sentry_api_urls.issues_url}?limit=20&query=is:unresolved%20NotFound" } diff --git a/spec/features/error_tracking/user_sees_error_index_spec.rb b/spec/features/error_tracking/user_sees_error_index_spec.rb index a4b15432ef3..bc6709c659d 100644 --- a/spec/features/error_tracking/user_sees_error_index_spec.rb +++ b/spec/features/error_tracking/user_sees_error_index_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'View error index page', :js, :use_clean_rails_memory_store_cachi let_it_be(:issues_response_body) { fixture_file('sentry/issues_sample_response.json') } let_it_be(:issues_response) { Gitlab::Json.parse(issues_response_body) } + let(:issues_api_url) { "#{sentry_api_urls.issues_url}?limit=20&query=is:unresolved" } before do @@ -41,7 +42,7 @@ RSpec.describe 'View error index page', :js, :use_clean_rails_memory_store_cachi context 'with error tracking settings disabled' do before do - project_error_tracking_settings.update(enabled: false) + project_error_tracking_settings.update!(enabled: false) sign_in(project.owner) visit project_error_tracking_index_path(project) diff --git a/spec/features/file_uploads/attachment_spec.rb b/spec/features/file_uploads/attachment_spec.rb new file mode 100644 index 00000000000..9ad404ce869 --- /dev/null +++ b/spec/features/file_uploads/attachment_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Upload an attachment', :api, :js do + include_context 'file upload requests helpers' + + let_it_be(:project) { create(:project) } + let_it_be(:user) { project.owner } + let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } + + let(:api_path) { "/projects/#{project_id}/uploads" } + let(:url) { capybara_url(api(api_path)) } + let(:file) { fixture_file_upload('spec/fixtures/dk.png') } + + subject do + HTTParty.post( + url, + headers: { 'PRIVATE-TOKEN' => personal_access_token.token }, + body: { file: file } + ) + end + + shared_examples 'for an attachment' do + it 'creates files' do + expect { subject } + .to change { Upload.count }.by(1) + end + + it { expect(subject.code).to eq(201) } + end + + context 'with an integer project ID' do + let(:project_id) { project.id } + + it_behaves_like 'handling file uploads', 'for an attachment' + end + + context 'with an encoded project ID' do + let(:project_id) { "#{project.namespace.path}%2F#{project.path}" } + + it_behaves_like 'handling file uploads', 'for an attachment' + end +end diff --git a/spec/features/file_uploads/maven_package_spec.rb b/spec/features/file_uploads/maven_package_spec.rb index e87eec58618..ab9f023bd8f 100644 --- a/spec/features/file_uploads/maven_package_spec.rb +++ b/spec/features/file_uploads/maven_package_spec.rb @@ -6,16 +6,17 @@ RSpec.describe 'Upload a maven package', :api, :js do include_context 'file upload requests helpers' let_it_be(:project) { create(:project) } - let_it_be(:user) { create(:user, :admin) } + let_it_be(:user) { project.owner } let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } - let(:api_path) { "/projects/#{project.id}/packages/maven/com/example/my-app/1.0/my-app-1.0-20180724.124855-1.jar" } + let(:project_id) { project.id } + let(:api_path) { "/projects/#{project_id}/packages/maven/com/example/my-app/1.0/my-app-1.0-20180724.124855-1.jar" } let(:url) { capybara_url(api(api_path, personal_access_token: personal_access_token)) } let(:file) { fixture_file_upload('spec/fixtures/dk.png') } subject { HTTParty.put(url, body: file.read) } - RSpec.shared_examples 'for a maven package' do + shared_examples 'for a maven package' do it 'creates package files' do expect { subject } .to change { Packages::Package.maven.count }.by(1) @@ -25,9 +26,9 @@ RSpec.describe 'Upload a maven package', :api, :js do it { expect(subject.code).to eq(200) } end - RSpec.shared_examples 'for a maven sha1' do + shared_examples 'for a maven sha1' do let(:dummy_package) { double(Packages::Package) } - let(:api_path) { "/projects/#{project.id}/packages/maven/com/example/my-app/1.0/my-app-1.0-20180724.124855-1.jar.sha1" } + let(:api_path) { "/projects/#{project_id}/packages/maven/com/example/my-app/1.0/my-app-1.0-20180724.124855-1.jar.sha1" } before do # The sha verification done by the maven api is between: @@ -42,8 +43,8 @@ RSpec.describe 'Upload a maven package', :api, :js do it { expect(subject.code).to eq(204) } end - RSpec.shared_examples 'for a maven md5' do - let(:api_path) { "/projects/#{project.id}/packages/maven/com/example/my-app/1.0/my-app-1.0-20180724.124855-1.jar.md5" } + shared_examples 'for a maven md5' do + let(:api_path) { "/projects/#{project_id}/packages/maven/com/example/my-app/1.0/my-app-1.0-20180724.124855-1.jar.md5" } let(:file) { StringIO.new('dummy_package') } it { expect(subject.code).to eq(200) } @@ -52,4 +53,10 @@ RSpec.describe 'Upload a maven package', :api, :js do it_behaves_like 'handling file uploads', 'for a maven package' it_behaves_like 'handling file uploads', 'for a maven sha1' it_behaves_like 'handling file uploads', 'for a maven md5' + + context 'with an encoded project ID' do + let(:project_id) { "#{project.namespace.path}%2F#{project.path}" } + + it_behaves_like 'handling file uploads', 'for a maven package' + end end diff --git a/spec/features/file_uploads/nuget_package_spec.rb b/spec/features/file_uploads/nuget_package_spec.rb index 6e05e5d1a6e..871c0274445 100644 --- a/spec/features/file_uploads/nuget_package_spec.rb +++ b/spec/features/file_uploads/nuget_package_spec.rb @@ -6,7 +6,7 @@ RSpec.describe 'Upload a nuget package', :api, :js do include_context 'file upload requests helpers' let_it_be(:project) { create(:project) } - let_it_be(:user) { create(:user, :admin) } + let_it_be(:user) { project.owner } let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } let(:api_path) { "/projects/#{project.id}/packages/nuget/" } @@ -21,7 +21,7 @@ RSpec.describe 'Upload a nuget package', :api, :js do ) end - RSpec.shared_examples 'for a nuget package' do + shared_examples 'for a nuget package' do it 'creates package files' do expect { subject } .to change { Packages::Package.nuget.count }.by(1) diff --git a/spec/features/file_uploads/rubygem_package_spec.rb b/spec/features/file_uploads/rubygem_package_spec.rb new file mode 100644 index 00000000000..4a5891fdfed --- /dev/null +++ b/spec/features/file_uploads/rubygem_package_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Upload a RubyGems package', :api, :js do + include_context 'file upload requests helpers' + + let_it_be(:project) { create(:project) } + let_it_be(:user) { project.owner } + let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } + + let(:api_path) { "/projects/#{project_id}/packages/rubygems/api/v1/gems" } + let(:url) { capybara_url(api(api_path)) } + let(:file) { fixture_file_upload('spec/fixtures/dk.png') } + + subject do + HTTParty.post( + url, + headers: { 'Authorization' => personal_access_token.token }, + body: { file: file } + ) + end + + shared_examples 'for a Rubygems package' do + it 'creates package files' do + expect { subject } + .to change { Packages::Package.rubygems.count }.by(1) + .and change { Packages::PackageFile.count }.by(1) + end + + it { expect(subject.code).to eq(201) } + end + + context 'with an integer project ID' do + let(:project_id) { project.id } + + it_behaves_like 'handling file uploads', 'for a Rubygems package' + end + + context 'with an encoded project ID' do + let(:project_id) { "#{project.namespace.path}%2F#{project.path}" } + + it_behaves_like 'handling file uploads', 'for a Rubygems package' + 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 b8797d9c139..6c25afdf6d4 100644 --- a/spec/features/frequently_visited_projects_and_groups_spec.rb +++ b/spec/features/frequently_visited_projects_and_groups_spec.rb @@ -6,6 +6,8 @@ RSpec.describe 'Frequently visited items', :js do let_it_be(:user) { create(:user) } before do + stub_feature_flags(combined_menu: false) + sign_in(user) end diff --git a/spec/features/gitlab_experiments_spec.rb b/spec/features/gitlab_experiments_spec.rb new file mode 100644 index 00000000000..76b418adcea --- /dev/null +++ b/spec/features/gitlab_experiments_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe "Gitlab::Experiment", :js do + # This is part of a set of tests that ensure that tracking remains + # consistent at the front end layer. Since we don't want to actually + # introduce an experiment in real code, we're going to simulate it + # here. + let(:user) { create(:user) } + + before do + admin = create(:admin) + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + stub_experiments(null_hypothesis: :candidate) + end + + describe 'with event tracking' do + it 'publishes the experiments that have been run to the client', :experiment do + allow_next_instance_of(Admin::AbuseReportsController) do |instance| + allow(instance).to receive(:index).and_wrap_original do |original| + instance.experiment(:null_hypothesis, user: instance.current_user) do |e| + e.use { original.call } + e.try { original.call } + end + end + end + + visit admin_abuse_reports_path + + expect(page).to have_content('Abuse Reports') + + published_experiments = page.evaluate_script('window.gon.experiment') + expect(published_experiments).to include({ + 'null_hypothesis' => { + 'experiment' => 'null_hypothesis', + 'key' => anything, + 'variant' => 'candidate' + } + }) + end + end +end diff --git a/spec/features/groups/board_spec.rb b/spec/features/groups/board_spec.rb index aab3f5e68d5..b4c60ff4fa3 100644 --- a/spec/features/groups/board_spec.rb +++ b/spec/features/groups/board_spec.rb @@ -33,7 +33,7 @@ RSpec.describe 'Group Boards' do find('.gl-new-dropdown-item button').click end - click_button 'Submit issue' + click_button 'Create issue' expect(page).to have_content(issue_title) end diff --git a/spec/features/groups/clusters/user_spec.rb b/spec/features/groups/clusters/user_spec.rb index 97f8864aab2..2a7ededa39b 100644 --- a/spec/features/groups/clusters/user_spec.rb +++ b/spec/features/groups/clusters/user_spec.rb @@ -97,7 +97,7 @@ RSpec.describe 'User Cluster', :js do context 'when user disables the cluster' do before do page.find(:css, '.js-cluster-enable-toggle-area .js-project-feature-toggle').click - page.within('.js-cluster-integration-form') { click_button 'Save changes' } + page.within('.js-cluster-details-form') { click_button 'Save changes' } end it 'user sees the successful message' do diff --git a/spec/features/groups/group_page_with_external_authorization_service_spec.rb b/spec/features/groups/group_page_with_external_authorization_service_spec.rb index 8ef1b60d8ca..187d878472e 100644 --- a/spec/features/groups/group_page_with_external_authorization_service_spec.rb +++ b/spec/features/groups/group_page_with_external_authorization_service_spec.rb @@ -19,7 +19,7 @@ RSpec.describe 'The group page' do expect(page).to have_link('Details') expect(page).to have_link('Activity') expect(page).to have_link('Issues') - expect(page).to have_link('Merge Requests') + expect(page).to have_link('Merge requests') expect(page).to have_link('Members') end end @@ -50,7 +50,7 @@ RSpec.describe 'The group page' do expect(page).not_to have_link('Contribution') expect(page).not_to have_link('Issues') - expect(page).not_to have_link('Merge Requests') + expect(page).not_to have_link('Merge requests') expect(page).to have_link('Members') end end diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb index 60cd1ebbbd7..00ad1006037 100644 --- a/spec/features/groups/group_settings_spec.rb +++ b/spec/features/groups/group_settings_spec.rb @@ -175,7 +175,7 @@ RSpec.describe 'Edit group settings' do end def updated_emails_disabled? - group.reload.clear_memoization(:emails_disabled) + group.reload.clear_memoization(:emails_disabled_memoized) group.emails_disabled? end end diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb index 8ecd2beba68..b0d2f90145f 100644 --- a/spec/features/groups/issues_spec.rb +++ b/spec/features/groups/issues_spec.rb @@ -108,7 +108,7 @@ RSpec.describe 'Group issues page' do it 'shows projects only with issues feature enabled', :js do find('.empty-state .js-lazy-loaded') - find('.new-project-item-link').click + find('.empty-state .new-project-item-link').click page.within('.select2-results') do expect(page).to have_content(project.full_name) diff --git a/spec/features/groups/labels/index_spec.rb b/spec/features/groups/labels/index_spec.rb index 3de29231f5c..68f03368989 100644 --- a/spec/features/groups/labels/index_spec.rb +++ b/spec/features/groups/labels/index_spec.rb @@ -24,6 +24,6 @@ RSpec.describe 'Group labels' do end it 'shows an edit label button', :js do - expect(page).to have_selector('.label-action.edit') + expect(page).to have_selector('.edit') end end diff --git a/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb index 38deee547a3..d31a7977f66 100644 --- a/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb +++ b/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js let_it_be(:user1) { create(:user, name: 'John Doe') } let_it_be(:group) { create(:group) } + let(:new_member) { create(:user, name: 'Mary Jane') } before do diff --git a/spec/features/groups/members/request_access_spec.rb b/spec/features/groups/members/request_access_spec.rb index 307cb63ec8e..827962fee61 100644 --- a/spec/features/groups/members/request_access_spec.rb +++ b/spec/features/groups/members/request_access_spec.rb @@ -15,7 +15,7 @@ RSpec.describe 'Groups > Members > Request access' do end it 'request access feature is disabled' do - group.update(request_access_enabled: false) + group.update!(request_access_enabled: false) visit group_path(group) expect(page).not_to have_content 'Request Access' diff --git a/spec/features/groups/merge_requests_spec.rb b/spec/features/groups/merge_requests_spec.rb index 43d4b6b23e0..f79c93157dc 100644 --- a/spec/features/groups/merge_requests_spec.rb +++ b/spec/features/groups/merge_requests_spec.rb @@ -27,7 +27,7 @@ RSpec.describe 'Group merge requests page' do end it 'ignores archived merge request count badges in navbar' do - expect(first(:link, text: 'Merge Requests').find('.badge').text).to eq("1") + expect(first(:link, text: 'Merge requests').find('.badge').text).to eq("1") end it 'ignores archived merge request count badges in state-filters' do diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb index 8d1008b98a6..1d9ac5ee1e9 100644 --- a/spec/features/groups/milestone_spec.rb +++ b/spec/features/groups/milestone_spec.rb @@ -133,7 +133,7 @@ RSpec.describe 'Group milestones' do href: project_issues_path(project, milestone_title: 'v1.0') ) expect(page).to have_link( - '0 Merge Requests', + '0 Merge requests', href: project_merge_requests_path(project, milestone_title: 'v1.0') ) expect(page).to have_link( @@ -145,7 +145,7 @@ RSpec.describe 'Group milestones' do href: issues_group_path(group, milestone_title: 'GL-113') ) expect(page).to have_link( - '0 Merge Requests', + '0 Merge requests', href: merge_requests_group_path(group, milestone_title: 'GL-113') ) end @@ -179,7 +179,7 @@ RSpec.describe 'Group milestones' do it 'renders the merge requests tab' do within('.js-milestone-tabs') do - click_link('Merge Requests') + click_link('Merge requests') end within('#tab-merge-requests') do diff --git a/spec/features/groups/navbar_spec.rb b/spec/features/groups/navbar_spec.rb index 7025874a4ff..021b1af54d4 100644 --- a/spec/features/groups/navbar_spec.rb +++ b/spec/features/groups/navbar_spec.rb @@ -30,7 +30,7 @@ RSpec.describe 'Group navbar' do ] }, { - nav_item: _('Merge Requests'), + nav_item: _('Merge requests'), nav_sub_items: [] }, (security_and_compliance_nav_item if Gitlab.ee?), @@ -68,7 +68,7 @@ RSpec.describe 'Group navbar' do before do stub_config(registry: { enabled: true }) - insert_container_nav(_('Kubernetes')) + insert_container_nav visit group_path(group) end @@ -80,7 +80,7 @@ RSpec.describe 'Group navbar' do before do stub_config(dependency_proxy: { enabled: true }) - insert_dependency_proxy_nav(_('Dependency Proxy')) + insert_dependency_proxy_nav visit group_path(group) end diff --git a/spec/features/groups/settings/user_searches_in_settings_spec.rb b/spec/features/groups/settings/user_searches_in_settings_spec.rb index 819d0c4faba..6d7a3871bb1 100644 --- a/spec/features/groups/settings/user_searches_in_settings_spec.rb +++ b/spec/features/groups/settings/user_searches_in_settings_spec.rb @@ -5,7 +5,6 @@ require 'spec_helper' RSpec.describe 'User searches group settings', :js do let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group) } - let_it_be(:project) { create(:project, :repository, namespace: group) } before do group.add_owner(user) @@ -13,9 +12,19 @@ RSpec.describe 'User searches group settings', :js do end context 'in general settings page' do - let(:visit_path) { edit_group_path(group) } + before do + visit edit_group_path(group) + end + + it_behaves_like 'can search settings', 'Naming', 'Permissions' + end + + context 'in Integrations page' do + before do + visit group_settings_integrations_path(group) + end - it_behaves_like 'can search settings with feature flag check', 'Naming', 'Permissions' + it_behaves_like 'can highlight results', 'set default configuration' end context 'in Repository page' do @@ -33,4 +42,12 @@ RSpec.describe 'User searches group settings', :js do it_behaves_like 'can search settings', 'Variables', 'Runners' end + + context 'in Packages & Registries page' do + before do + visit group_settings_packages_and_registries_path(group) + end + + it_behaves_like 'can highlight results', 'GitLab Packages' + end end diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index 28b22860f0a..33d2ac50628 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -141,6 +141,30 @@ RSpec.describe 'Group' do end end end + + describe 'showing recaptcha on group creation when it is enabled' do + before do + stub_application_setting(recaptcha_enabled: true) + allow(Gitlab::Recaptcha).to receive(:load_configurations!) + visit new_group_path + end + + it 'renders recaptcha' do + expect(page).to have_css('.recaptcha') + end + end + + describe 'not showing recaptcha on group creation when it is disabled' do + before do + stub_feature_flags(recaptcha_on_top_level_group_creation: false) + stub_application_setting(recaptcha_enabled: true) + visit new_group_path + end + + it 'does not render recaptcha' do + expect(page).not_to have_css('.recaptcha') + end + end end describe 'create a nested group' do @@ -189,6 +213,46 @@ RSpec.describe 'Group' do expect(page).to have_content("Group 'bar' was successfully created.") end end + + context 'when recaptcha is enabled' do + before do + stub_application_setting(recaptcha_enabled: true) + allow(Gitlab::Recaptcha).to receive(:load_configurations!) + end + + context 'when creating subgroup' do + let(:path) { new_group_path(group, parent_id: group.id) } + + it 'does not render recaptcha' do + visit path + + expect(page).not_to have_css('.recaptcha') + end + end + end + + describe 'real-time group url validation', :js do + let_it_be(:subgroup) { create(:group, path: 'sub', parent: group) } + + before do + group.add_owner(user) + visit new_group_path(parent_id: group.id) + end + + it 'shows a message if group url is available' do + fill_in 'Group URL', with: group.path + wait_for_requests + + expect(page).to have_content('Group path is available') + end + + it 'shows an error if group url is taken' do + fill_in 'Group URL', with: subgroup.path + wait_for_requests + + expect(page).to have_content('Group path is already taken') + end + end end it 'checks permissions to avoid exposing groups by parent_id' do @@ -203,6 +267,7 @@ RSpec.describe 'Group' do describe 'group edit', :js do let_it_be(:group) { create(:group, :public) } + let(:path) { edit_group_path(group) } let(:new_name) { 'new-name' } @@ -248,6 +313,7 @@ RSpec.describe 'Group' do describe 'group page with markdown description' do let_it_be(:group) { create(:group) } + let(:path) { group_path(group) } before do diff --git a/spec/features/ide/clientside_preview_csp_spec.rb b/spec/features/ide/clientside_preview_csp_spec.rb index eadcb9cd008..559edb8bf53 100644 --- a/spec/features/ide/clientside_preview_csp_spec.rb +++ b/spec/features/ide/clientside_preview_csp_spec.rb @@ -7,9 +7,7 @@ RSpec.describe 'IDE Clientside Preview CSP' do shared_context 'disable feature' do before do - allow_next_instance_of(ApplicationSetting) do |instance| - allow(instance).to receive(:web_ide_clientside_preview_enabled?).and_return(false) - end + stub_application_setting(web_ide_clientside_preview_enabled: false) end end @@ -24,10 +22,8 @@ RSpec.describe 'IDE Clientside Preview CSP' do end before do - allow_next_instance_of(ApplicationSetting) do |instance| - allow(instance).to receive(:web_ide_clientside_preview_enabled?).and_return(true) - allow(instance).to receive(:web_ide_clientside_preview_bundler_url).and_return(whitelisted_url) - end + stub_application_setting(web_ide_clientside_preview_enabled: true) + stub_application_setting(web_ide_clientside_preview_bundler_url: whitelisted_url) sign_in(user) end diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb index dfd6211a683..520cf850da2 100644 --- a/spec/features/import/manifest_import_spec.rb +++ b/spec/features/import/manifest_import_spec.rb @@ -37,11 +37,20 @@ RSpec.describe 'Import multiple repositories by uploading a manifest file', :js wait_for_requests page.within(second_row) do - expect(page).to have_content 'Done' + expect(page).to have_content 'Complete' expect(page).to have_content("#{group.full_path}/build/blueprint") end end + it 'renders an error if the remote url scheme starts with javascript' do + visit new_import_manifest_path + + attach_file('manifest', Rails.root.join('spec/fixtures/unsafe_javascript.xml')) + click_on 'List available repositories' + + expect(page).to have_content 'Make sure the url does not start with javascript' + end + it 'renders an error if invalid file was provided' do visit new_import_manifest_path diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb index 2ceffa896eb..e9960802378 100644 --- a/spec/features/invites_spec.rb +++ b/spec/features/invites_spec.rb @@ -50,21 +50,23 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do end it 'renders sign in page with sign in notice' do - expect(current_path).to eq(new_user_session_path) - expect(page).to have_content('To accept this invitation, sign in') + expect(current_path).to eq(new_user_registration_path) + expect(page).to have_content('To accept this invitation, create an account or sign in') end it 'pre-fills the "Username or email" field on the sign in box with the invite_email from the invite' do + click_link 'Sign in' + expect(find_field('Username or email').value).to eq(group_invite.invite_email) end it 'pre-fills the Email field on the sign up box with the invite_email from the invite' do - click_link 'Register now' - expect(find_field('Email').value).to eq(group_invite.invite_email) end it 'sign in, grants access and redirects to group page' do + click_link 'Sign in' + fill_in_sign_in_form(user) expect(current_path).to eq(group_path(group)) @@ -85,20 +87,19 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do end end - context 'when inviting a user' do + context 'when inviting an unregistered user' do let(:new_user) { build_stubbed(:user) } let(:invite_email) { new_user.email } let(:group_invite) { create(:group_member, :invited, group: group, invite_email: invite_email, created_by: owner) } let!(:project_invite) { create(:project_member, :invited, project: project, invite_email: invite_email) } - context 'when user has not signed in yet' do + context 'when registering using invitation email' do before do stub_application_setting(send_user_confirmation_email: send_email_confirmation) visit invite_path(group_invite.raw_invite_token) - click_link 'Register now' end - context 'with admin appoval required enabled' do + context 'with admin approval required enabled' do before do stub_application_setting(require_admin_approval_after_user_signup: true) end diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb index aa61aff3b05..80bf964e2ee 100644 --- a/spec/features/issues/bulk_assignment_labels_spec.rb +++ b/spec/features/issues/bulk_assignment_labels_spec.rb @@ -295,8 +295,8 @@ RSpec.describe 'Issues > Labels bulk assignment' do before do issue1.milestone = milestone issue2.milestone = milestone - issue1.save - issue2.save + issue1.save! + issue2.save! issue1.labels << bug issue2.labels << feature 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 a4e9df604a9..34d78880991 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 @@ -18,10 +18,6 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j end end - before do - stub_feature_flags(remove_resolve_note: false) - end - describe 'as a user with access to the project' do before do project.add_maintainer(user) @@ -37,7 +33,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j context 'resolving the thread' do before do - click_button 'Resolve thread' + find('button[data-qa-selector="resolve_discussion_button"]').click end it 'hides the link for creating a new issue' do diff --git a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb index 99dc71f0559..ac3471e8401 100644 --- a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb +++ b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb @@ -14,10 +14,6 @@ RSpec.describe 'Resolve an open thread in a merge request by creating an issue', "a[title=\"#{title}\"][href=\"#{url}\"]" end - before do - stub_feature_flags(remove_resolve_note: false) - end - describe 'As a user with access to the project' do before do project.add_maintainer(user) @@ -39,7 +35,7 @@ RSpec.describe 'Resolve an open thread in a merge request by creating an issue', context 'resolving the thread' do before do - click_button 'Resolve thread' + find('button[data-qa-selector="resolve_discussion_button"]').click end it 'hides the link for creating a new issue' do diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index dac066856c0..5ca20028485 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -156,7 +156,7 @@ RSpec.describe 'New/edit issue', :js do expect(page.all('input[name="issue[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s) expect(page.all('input[name="issue[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s) - click_button 'Submit issue' + click_button 'Create issue' page.within '.issuable-sidebar' do page.within '.assignee' do diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb index e6ebc37ba59..0cefbae4d37 100644 --- a/spec/features/issues/gfm_autocomplete_spec.rb +++ b/spec/features/issues/gfm_autocomplete_spec.rb @@ -3,16 +3,23 @@ require 'spec_helper' RSpec.describe 'GFM autocomplete', :js do - let_it_be(:user_xss_title) { 'eve <img src=x onerror=alert(2)<img src=x onerror=alert(1)>' } - let_it_be(:user_xss) { create(:user, name: user_xss_title, username: 'xss.user') } let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') } let_it_be(:user2) { create(:user, name: 'Marge Simpson', username: 'msimpson') } + let_it_be(:group) { create(:group, name: 'Ancestor') } let_it_be(:child_group) { create(:group, parent: group, name: 'My group') } let_it_be(:project) { create(:project, group: child_group) } + + let_it_be(:issue) { create(:issue, project: project, assignees: [user]) } let_it_be(:label) { create(:label, project: project, title: 'special+') } + let_it_be(:label_scoped) { create(:label, project: project, title: 'scoped::label') } + let_it_be(:label_with_spaces) { create(:label, project: project, title: 'Accepting merge requests') } + let_it_be(:snippet) { create(:project_snippet, project: project, title: 'code snippet') } - let(:issue) { create(:issue, project: project) } + let_it_be(:user_xss_title) { 'eve <img src=x onerror=alert(2)<img src=x onerror=alert(1)>' } + let_it_be(:user_xss) { create(:user, name: user_xss_title, username: 'xss.user') } + let_it_be(:label_xss_title) { 'alert label <img src=x onerror="alert(\'Hello xss\');" a' } + let_it_be(:label_xss) { create(:label, project: project, title: label_xss_title) } before_all do project.add_maintainer(user) @@ -21,418 +28,366 @@ RSpec.describe 'GFM autocomplete', :js do end describe 'when tribute_autocomplete feature flag is off' do - before do - stub_feature_flags(tribute_autocomplete: false) - - sign_in(user) - visit project_issue_path(project, issue) - - wait_for_requests - end - - it 'updates issue description with GFM reference' do - click_button 'Edit title and description' - - wait_for_requests - - fill_in 'Description', with: "@#{user.name[0...3]}" - - wait_for_requests - - find_highlighted_autocomplete_item.click - - click_button 'Save changes' - - wait_for_requests - - expect(find('.description')).to have_text(user.to_reference) - end - - it 'opens quick action autocomplete when updating description' do - click_button 'Edit title and description' - - fill_in 'Description', with: '/' - - expect(find_autocomplete_menu).to be_visible - end - - it 'opens autocomplete menu when field starts with text' do - fill_in 'Comment', with: '@' - - expect(find_autocomplete_menu).to be_visible - end - - it 'opens autocomplete menu for Issues when field starts with text with item escaping HTML characters' do - issue_xss_title = 'This will execute alert<img src=x onerror=alert(2)<img src=x onerror=alert(1)>' - create(:issue, project: project, title: issue_xss_title) - - fill_in 'Comment', with: '#' - - wait_for_requests + describe 'new issue page' do + before do + stub_feature_flags(tribute_autocomplete: false) - expect(find_autocomplete_menu).to have_text(issue_xss_title) - end + sign_in(user) + visit new_project_issue_path(project) - it 'opens autocomplete menu for Username when field starts with text with item escaping HTML characters' do - fill_in 'Comment', with: '@ev' + wait_for_requests + end - wait_for_requests + it 'allows quick actions' do + fill_in 'Description', with: '/' - expect(find_highlighted_autocomplete_item).to have_text(user_xss.username) + expect(find_autocomplete_menu).to be_visible + end end - it 'opens autocomplete menu for Milestone when field starts with text with item escaping HTML characters' do - milestone_xss_title = 'alert milestone <img src=x onerror="alert(\'Hello xss\');" a' - create(:milestone, project: project, title: milestone_xss_title) + describe 'issue description' do + let(:issue_to_edit) { create(:issue, project: project) } - fill_in 'Comment', with: '%' + before do + stub_feature_flags(tribute_autocomplete: false) - wait_for_requests + sign_in(user) + visit project_issue_path(project, issue_to_edit) - expect(find_autocomplete_menu).to have_text('alert milestone') - end + wait_for_requests + end - it 'doesnt open autocomplete menu character is prefixed with text' do - fill_in 'Comment', with: 'testing@' + it 'updates with GFM reference' do + click_button 'Edit title and description' - expect(page).not_to have_css('.atwho-view') - end + wait_for_requests - it 'doesnt select the first item for non-assignee dropdowns' do - fill_in 'Comment', with: ':' + fill_in 'Description', with: "@#{user.name[0...3]}" - wait_for_requests + wait_for_requests - expect(find_autocomplete_menu).not_to have_css('.cur') - end + find_highlighted_autocomplete_item.click - it 'does not open autocomplete menu when ":" is prefixed by a number and letters' do - # Number. - fill_in 'Comment', with: '7:' - expect(page).not_to have_css('.atwho-view') + click_button 'Save changes' - # ASCII letter. - fill_in 'Comment', with: 'w:' - expect(page).not_to have_css('.atwho-view') + wait_for_requests - # Non-ASCII letter. - fill_in 'Comment', with: 'Ё:' - expect(page).not_to have_css('.atwho-view') - end + expect(find('.description')).to have_text(user.to_reference) + end - it 'selects the first item for assignee dropdowns' do - fill_in 'Comment', with: '@' + it 'allows quick actions' do + click_button 'Edit title and description' - wait_for_requests + fill_in 'Description', with: '/' - expect(find_autocomplete_menu).to have_css('.cur:first-of-type') + expect(find_autocomplete_menu).to be_visible + end end - it 'includes items for assignee dropdowns with non-ASCII characters in name' do - fill_in 'Comment', with: "@#{user.name[0...8]}" + describe 'issue comment' do + before do + stub_feature_flags(tribute_autocomplete: false) - wait_for_requests + sign_in(user) + visit project_issue_path(project, issue) - expect(find_autocomplete_menu).to have_text(user.name) - end + wait_for_requests + end - it 'searches across full name for assignees' do - fill_in 'Comment', with: '@speciąlsome' + describe 'triggering autocomplete' do + it 'only opens autocomplete menu when trigger character is after whitespace', :aggregate_failures do + fill_in 'Comment', with: 'testing@' + expect(page).not_to have_css('.atwho-view') - wait_for_requests + fill_in 'Comment', with: '@@' + expect(page).not_to have_css('.atwho-view') - expect(find_highlighted_autocomplete_item).to have_text(user.name) - end + fill_in 'Comment', with: "@#{user.username[0..2]}!" + expect(page).not_to have_css('.atwho-view') - it 'shows names that start with the query as the top result' do - fill_in 'Comment', with: '@mar' + fill_in 'Comment', with: "hello:#{user.username[0..2]}" + expect(page).not_to have_css('.atwho-view') - wait_for_requests + fill_in 'Comment', with: '7:' + expect(page).not_to have_css('.atwho-view') - expect(find_highlighted_autocomplete_item).to have_text(user2.name) - end - - it 'shows usernames that start with the query as the top result' do - fill_in 'Comment', with: '@msi' + fill_in 'Comment', with: 'w:' + expect(page).not_to have_css('.atwho-view') - wait_for_requests + fill_in 'Comment', with: 'Ё:' + expect(page).not_to have_css('.atwho-view') - expect(find_highlighted_autocomplete_item).to have_text(user2.name) - end + fill_in 'Comment', with: "test\n\n@" + expect(find_autocomplete_menu).to be_visible + end + end - # Regression test for https://gitlab.com/gitlab-org/gitlab/-/issues/321925 - it 'shows username when pasting then pressing Enter' do - fill_in 'Comment', with: "@#{user.username}\n" + context 'xss checks' do + it 'opens autocomplete menu for Issues when field starts with text with item escaping HTML characters' do + issue_xss_title = 'This will execute alert<img src=x onerror=alert(2)<img src=x onerror=alert(1)>' + create(:issue, project: project, title: issue_xss_title) - expect(find_field('Comment').value).to have_text "@#{user.username}" - end + fill_in 'Comment', with: '#' - it 'does not show `@undefined` when pressing `@` then Enter' do - fill_in 'Comment', with: "@\n" + wait_for_requests - expect(find_field('Comment').value).to have_text '@' - expect(find_field('Comment').value).not_to have_text '@undefined' - end + expect(find_autocomplete_menu).to have_text(issue_xss_title) + end - it 'selects the first item for non-assignee dropdowns if a query is entered' do - fill_in 'Comment', with: ':1' + it 'opens autocomplete menu for Username when field starts with text with item escaping HTML characters' do + fill_in 'Comment', with: '@ev' - wait_for_requests + wait_for_requests - expect(find_autocomplete_menu).to have_css('.cur:first-of-type') - end + expect(find_highlighted_autocomplete_item).to have_text(user_xss.username) + end - context 'if a selected value has special characters' do - it 'wraps the result in double quotes' do - fill_in 'Comment', with: "~#{label.title[0]}" + it 'opens autocomplete menu for Milestone when field starts with text with item escaping HTML characters' do + milestone_xss_title = 'alert milestone <img src=x onerror="alert(\'Hello xss\');" a' + create(:milestone, project: project, title: milestone_xss_title) - find_highlighted_autocomplete_item.click + fill_in 'Comment', with: '%' - expect(find_field('Comment').value).to have_text("~\"#{label.title}\"") - end + wait_for_requests - it "shows dropdown after a new line" do - fill_in 'Comment', with: "test\n\n@" + expect(find_autocomplete_menu).to have_text('alert milestone') + end - expect(find_autocomplete_menu).to be_visible - end + it 'opens autocomplete menu for Labels when field starts with text with item escaping HTML characters' do + fill_in 'Comment', with: '~' - it "does not show dropdown when preceded with a special character" do - fill_in 'Comment', with: '@@' + wait_for_requests - expect(page).not_to have_css('.atwho-view') + expect(find_autocomplete_menu).to have_text('alert label') + end end - it 'doesn\'t wrap for assignee values' do - fill_in 'Comment', with: "@#{user.username[0]}" + describe 'autocomplete highlighting' do + it 'auto-selects the first item when there is a query, and only for assignees with no query', :aggregate_failures do + fill_in 'Comment', with: ':' + wait_for_requests + expect(find_autocomplete_menu).not_to have_css('.cur') - find_highlighted_autocomplete_item.click + fill_in 'Comment', with: ':1' + wait_for_requests + expect(find_autocomplete_menu).to have_css('.cur:first-of-type') - expect(find_field('Comment').value).to have_text("@#{user.username}") + fill_in 'Comment', with: '@' + wait_for_requests + expect(find_autocomplete_menu).to have_css('.cur:first-of-type') + end end - it 'doesn\'t wrap for emoji values' do - fill_in 'Comment', with: ':cartwheel_' - - find_highlighted_autocomplete_item.click + describe 'assignees' do + it 'does not wrap with quotes for assignee values' do + fill_in 'Comment', with: "@#{user.username[0]}" - expect(find_field('Comment').value).to have_text('cartwheel_tone1') - end + find_highlighted_autocomplete_item.click - it 'doesn\'t open autocomplete after non-word character' do - fill_in 'Comment', with: "@#{user.username[0..2]}!" + expect(find_field('Comment').value).to have_text("@#{user.username}") + end - expect(page).not_to have_css('.atwho-view') - end + it 'includes items for assignee dropdowns with non-ASCII characters in name' do + fill_in 'Comment', with: "@#{user.name[0...8]}" - it 'doesn\'t open autocomplete if there is no space before' do - fill_in 'Comment', with: "hello:#{user.username[0..2]}" + wait_for_requests - expect(page).not_to have_css('.atwho-view') - end + expect(find_autocomplete_menu).to have_text(user.name) + end - it 'triggers autocomplete after selecting a quick action' do - fill_in 'Comment', with: '/as' + it 'searches across full name for assignees' do + fill_in 'Comment', with: '@speciąlsome' - find_highlighted_autocomplete_item.click + wait_for_requests - expect(find_autocomplete_menu).to have_text(user.username) - end + expect(find_highlighted_autocomplete_item).to have_text(user.name) + end - it 'does not limit quick actions autocomplete list to 5' do - fill_in 'Comment', with: '/' + it 'shows names that start with the query as the top result' do + fill_in 'Comment', with: '@mar' - expect(find_autocomplete_menu).to have_css('li', minimum: 6) - end - end + wait_for_requests - context 'assignees' do - let(:issue_assignee) { create(:issue, project: project) } - let(:unassigned_user) { create(:user) } + expect(find_highlighted_autocomplete_item).to have_text(user2.name) + end - before do - issue_assignee.update(assignees: [user]) + it 'shows usernames that start with the query as the top result' do + fill_in 'Comment', with: '@msi' - project.add_maintainer(unassigned_user) - end + wait_for_requests - it 'lists users who are currently not assigned to the issue when using /assign' do - visit project_issue_path(project, issue_assignee) + expect(find_highlighted_autocomplete_item).to have_text(user2.name) + end - fill_in 'Comment', with: '/as' + # Regression test for https://gitlab.com/gitlab-org/gitlab/-/issues/321925 + it 'shows username when pasting then pressing Enter' do + fill_in 'Comment', with: "@#{user.username}\n" - find_highlighted_autocomplete_item.click + expect(find_field('Comment').value).to have_text "@#{user.username}" + end - expect(find_autocomplete_menu).not_to have_text(user.username) - expect(find_autocomplete_menu).to have_text(unassigned_user.username) - end + it 'does not show `@undefined` when pressing `@` then Enter' do + fill_in 'Comment', with: "@\n" - it 'shows dropdown on new issue form' do - visit new_project_issue_path(project) + expect(find_field('Comment').value).to have_text '@' + expect(find_field('Comment').value).not_to have_text '@undefined' + end - fill_in 'Description', with: '/ass' + context 'when /assign quick action is selected' do + it 'triggers user autocomplete and lists users who are currently not assigned to the issue' do + fill_in 'Comment', with: '/as' - find_highlighted_autocomplete_item.click + find_highlighted_autocomplete_item.click - expect(find_autocomplete_menu).to have_text(unassigned_user.username) - expect(find_autocomplete_menu).to have_text(user.username) + expect(find_autocomplete_menu).not_to have_text(user.username) + expect(find_autocomplete_menu).to have_text(user2.username) + end + end end - end - - context 'labels' do - it 'opens autocomplete menu for Labels when field starts with text with item escaping HTML characters' do - label_xss_title = 'alert label <img src=x onerror="alert(\'Hello xss\');" a' - create(:label, project: project, title: label_xss_title) - fill_in 'Comment', with: '~' - - wait_for_requests + context 'if a selected value has special characters' do + it 'wraps the result in double quotes' do + fill_in 'Comment', with: "~#{label.title[0..2]}" - expect(find_autocomplete_menu).to have_text('alert label') - end + find_highlighted_autocomplete_item.click - it 'allows colons when autocompleting scoped labels' do - create(:label, project: project, title: 'scoped:label') + expect(find_field('Comment').value).to have_text("~\"#{label.title}\"") + end - fill_in 'Comment', with: '~scoped:' + it 'doesn\'t wrap for emoji values' do + fill_in 'Comment', with: ':cartwheel_' - wait_for_requests + find_highlighted_autocomplete_item.click - expect(find_autocomplete_menu).to have_text('scoped:label') + expect(find_field('Comment').value).to have_text('cartwheel_tone1') + end end - it 'allows colons when autocompleting scoped labels with double colons' do - create(:label, project: project, title: 'scoped::label') + context 'quick actions' do + it 'does not limit quick actions autocomplete list to 5' do + fill_in 'Comment', with: '/' - fill_in 'Comment', with: '~scoped::' - - wait_for_requests - - expect(find_autocomplete_menu).to have_text('scoped::label') + expect(find_autocomplete_menu).to have_css('li', minimum: 6) + end end - it 'allows spaces when autocompleting multi-word labels' do - create(:label, project: project, title: 'Accepting merge requests') + context 'labels' do + it 'allows colons when autocompleting scoped labels' do + fill_in 'Comment', with: '~scoped:' - fill_in 'Comment', with: '~Accepting merge' + wait_for_requests - wait_for_requests + expect(find_autocomplete_menu).to have_text('scoped::label') + end - expect(find_autocomplete_menu).to have_text('Accepting merge requests') - end + it 'allows spaces when autocompleting multi-word labels' do + fill_in 'Comment', with: '~Accepting merge' - it 'only autocompletes the latest label' do - create(:label, project: project, title: 'Accepting merge requests') - create(:label, project: project, title: 'Accepting job applicants') + wait_for_requests - fill_in 'Comment', with: '~Accepting merge requests foo bar ~Accepting job' + expect(find_autocomplete_menu).to have_text('Accepting merge requests') + end - wait_for_requests + it 'only autocompletes the last label' do + fill_in 'Comment', with: '~scoped:: foo bar ~Accepting merge' - expect(find_autocomplete_menu).to have_text('Accepting job applicants') - end + wait_for_requests - it 'does not autocomplete labels if no tilde is typed' do - create(:label, project: project, title: 'Accepting merge requests') + expect(find_autocomplete_menu).to have_text('Accepting merge requests') + end - fill_in 'Comment', with: 'Accepting merge' + it 'does not autocomplete labels if no tilde is typed' do + fill_in 'Comment', with: 'Accepting merge' - wait_for_requests + wait_for_requests - expect(page).not_to have_css('.atwho-view') + expect(page).not_to have_css('.atwho-view') + end end - end - context 'when other notes are destroyed' do - let!(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) } + context 'when other notes are destroyed' do + let!(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) } - # This is meant to protect against this issue https://gitlab.com/gitlab-org/gitlab/-/issues/228729 - it 'keeps autocomplete key listeners' do - visit project_issue_path(project, issue) - note = find_field('Comment') + # This is meant to protect against this issue https://gitlab.com/gitlab-org/gitlab/-/issues/228729 + it 'keeps autocomplete key listeners' do + note = find_field('Comment') - start_comment_with_emoji(note, '.atwho-view li') + start_comment_with_emoji(note, '.atwho-view li') - start_and_cancel_discussion + start_and_cancel_discussion - note.fill_in(with: '') - start_comment_with_emoji(note, '.atwho-view li') - note.native.send_keys(:enter) + note.fill_in(with: '') + start_comment_with_emoji(note, '.atwho-view li') + note.native.send_keys(:enter) - expect(note.value).to eql('Hello :100: ') + expect(note.value).to eql('Hello :100: ') + end end - end - shared_examples 'autocomplete suggestions' do - it 'suggests objects correctly' do - fill_in 'Comment', with: object.class.reference_prefix + shared_examples 'autocomplete suggestions' do + it 'suggests objects correctly' do + fill_in 'Comment', with: object.class.reference_prefix - find_autocomplete_menu.find('li').click + find_autocomplete_menu.find('li').click - expect(find_field('Comment').value).to have_text(expected_body) + expect(find_field('Comment').value).to have_text(expected_body) + end end - end - context 'issues' do - let(:object) { issue } - let(:expected_body) { object.to_reference } + context 'issues' do + let(:object) { issue } + let(:expected_body) { object.to_reference } - it_behaves_like 'autocomplete suggestions' - end - - context 'merge requests' do - let(:object) { create(:merge_request, source_project: project) } - let(:expected_body) { object.to_reference } - - it_behaves_like 'autocomplete suggestions' - end + it_behaves_like 'autocomplete suggestions' + end - context 'project snippets' do - let!(:object) { create(:project_snippet, project: project, title: 'code snippet') } - let(:expected_body) { object.to_reference } + context 'merge requests' do + let(:object) { create(:merge_request, source_project: project) } + let(:expected_body) { object.to_reference } - it_behaves_like 'autocomplete suggestions' - end + it_behaves_like 'autocomplete suggestions' + end - context 'label' do - let!(:object) { label } - let(:expected_body) { object.title } + context 'project snippets' do + let!(:object) { snippet } + let(:expected_body) { object.to_reference } - it_behaves_like 'autocomplete suggestions' - end + it_behaves_like 'autocomplete suggestions' + end - context 'milestone' do - let_it_be(:milestone_expired) { create(:milestone, project: project, due_date: 5.days.ago) } - let_it_be(:milestone_no_duedate) { create(:milestone, project: project, title: 'Foo - No due date') } - let_it_be(:milestone1) { create(:milestone, project: project, title: 'Milestone-1', due_date: 20.days.from_now) } - let_it_be(:milestone2) { create(:milestone, project: project, title: 'Milestone-2', due_date: 15.days.from_now) } - let_it_be(:milestone3) { create(:milestone, project: project, title: 'Milestone-3', due_date: 10.days.from_now) } + context 'milestone' do + let_it_be(:milestone_expired) { create(:milestone, project: project, due_date: 5.days.ago) } + let_it_be(:milestone_no_duedate) { create(:milestone, project: project, title: 'Foo - No due date') } + let_it_be(:milestone1) { create(:milestone, project: project, title: 'Milestone-1', due_date: 20.days.from_now) } + let_it_be(:milestone2) { create(:milestone, project: project, title: 'Milestone-2', due_date: 15.days.from_now) } + let_it_be(:milestone3) { create(:milestone, project: project, title: 'Milestone-3', due_date: 10.days.from_now) } - before do - fill_in 'Comment', with: '/milestone %' + before do + fill_in 'Comment', with: '/milestone %' - wait_for_requests - end + wait_for_requests + end - it 'shows milestons list in the autocomplete menu' do - page.within(find_autocomplete_menu) do - expect(page).to have_selector('li', count: 5) + it 'shows milestons list in the autocomplete menu' do + page.within(find_autocomplete_menu) do + expect(page).to have_selector('li', count: 5) + end end - end - it 'shows expired milestone at the bottom of the list' do - page.within(find_autocomplete_menu) do - expect(page.find('li:last-child')).to have_content milestone_expired.title + it 'shows expired milestone at the bottom of the list' do + page.within(find_autocomplete_menu) do + expect(page.find('li:last-child')).to have_content milestone_expired.title + end end - end - it 'shows milestone due earliest at the top of the list' do - page.within(find_autocomplete_menu) do - aggregate_failures do - expect(page.all('li')[0]).to have_content milestone3.title - expect(page.all('li')[1]).to have_content milestone2.title - expect(page.all('li')[2]).to have_content milestone1.title - expect(page.all('li')[3]).to have_content milestone_no_duedate.title + it 'shows milestone due earliest at the top of the list' do + page.within(find_autocomplete_menu) do + aggregate_failures do + expect(page.all('li')[0]).to have_content milestone3.title + expect(page.all('li')[1]).to have_content milestone2.title + expect(page.all('li')[2]).to have_content milestone1.title + expect(page.all('li')[3]).to have_content milestone_no_duedate.title + end end end end @@ -440,346 +395,303 @@ RSpec.describe 'GFM autocomplete', :js do end describe 'when tribute_autocomplete feature flag is on' do - before do - stub_feature_flags(tribute_autocomplete: true) - - sign_in(user) - visit project_issue_path(project, issue) - - wait_for_requests - end + describe 'issue description' do + let(:issue_to_edit) { create(:issue, project: project) } - it 'updates issue description with GFM reference' do - click_button 'Edit title and description' - - wait_for_requests - - fill_in 'Description', with: "@#{user.name[0...3]}" - - wait_for_requests + before do + stub_feature_flags(tribute_autocomplete: true) - find_highlighted_tribute_autocomplete_menu.click + sign_in(user) + visit project_issue_path(project, issue_to_edit) - click_button 'Save changes' + wait_for_requests + end - wait_for_requests + it 'updates with GFM reference' do + click_button 'Edit title and description' - expect(find('.description')).to have_text(user.to_reference) - end + wait_for_requests - it 'opens autocomplete menu when field starts with text' do - fill_in 'Comment', with: '@' + fill_in 'Description', with: "@#{user.name[0...3]}" - expect(find_tribute_autocomplete_menu).to be_visible - end + wait_for_requests - it 'opens autocomplete menu for Issues when field starts with text with item escaping HTML characters' do - issue_xss_title = 'This will execute alert<img src=x onerror=alert(2)<img src=x onerror=alert(1)>' - create(:issue, project: project, title: issue_xss_title) + find_highlighted_tribute_autocomplete_menu.click - fill_in 'Comment', with: '#' + click_button 'Save changes' - wait_for_requests + wait_for_requests - expect(find_tribute_autocomplete_menu).to have_text(issue_xss_title) + expect(find('.description')).to have_text(user.to_reference) + end end - it 'opens autocomplete menu for Username when field starts with text with item escaping HTML characters' do - fill_in 'Comment', with: '@ev' - - wait_for_requests - - expect(find_tribute_autocomplete_menu).to have_text(user_xss.username) - end + describe 'issue comment' do + before do + stub_feature_flags(tribute_autocomplete: true) - it 'opens autocomplete menu for Milestone when field starts with text with item escaping HTML characters' do - milestone_xss_title = 'alert milestone <img src=x onerror="alert(\'Hello xss\');" a' - create(:milestone, project: project, title: milestone_xss_title) + sign_in(user) + visit project_issue_path(project, issue) - fill_in 'Comment', with: '%' + wait_for_requests + end - wait_for_requests + describe 'triggering autocomplete' do + it 'only opens autocomplete menu when trigger character is after whitespace', :aggregate_failures do + fill_in 'Comment', with: 'testing@' + expect(page).not_to have_css('.tribute-container') - expect(find_tribute_autocomplete_menu).to have_text('alert milestone') - end + fill_in 'Comment', with: "hello:#{user.username[0..2]}" + expect(page).not_to have_css('.tribute-container') - it 'does not open autocomplete menu when trigger character is prefixed with text' do - fill_in 'Comment', with: 'testing@' + fill_in 'Comment', with: '7:' + expect(page).not_to have_css('.tribute-container') - expect(page).not_to have_css('.tribute-container') - end + fill_in 'Comment', with: 'w:' + expect(page).not_to have_css('.tribute-container') - it 'does not open autocomplete menu when ":" is prefixed by a number and letters' do - # Number. - fill_in 'Comment', with: '7:' - expect(page).not_to have_css('.tribute-container') + fill_in 'Comment', with: 'Ё:' + expect(page).not_to have_css('.tribute-container') - # ASCII letter. - fill_in 'Comment', with: 'w:' - expect(page).not_to have_css('.tribute-container') + fill_in 'Comment', with: "test\n\n@" + expect(find_tribute_autocomplete_menu).to be_visible + end + end - # Non-ASCII letter. - fill_in 'Comment', with: 'Ё:' - expect(page).not_to have_css('.tribute-container') - end + context 'xss checks' do + it 'opens autocomplete menu for Issues when field starts with text with item escaping HTML characters' do + issue_xss_title = 'This will execute alert<img src=x onerror=alert(2)<img src=x onerror=alert(1)>' + create(:issue, project: project, title: issue_xss_title) - it 'selects the first item for assignee dropdowns' do - fill_in 'Comment', with: '@' + fill_in 'Comment', with: '#' - wait_for_requests + wait_for_requests - expect(find_tribute_autocomplete_menu).to have_css('.highlight:first-of-type') - end + expect(find_tribute_autocomplete_menu).to have_text(issue_xss_title) + end - it 'includes items for assignee dropdowns with non-ASCII characters in name' do - fill_in 'Comment', with: "@#{user.name[0...8]}" + it 'opens autocomplete menu for Username when field starts with text with item escaping HTML characters' do + fill_in 'Comment', with: '@ev' - wait_for_requests + wait_for_requests - expect(find_tribute_autocomplete_menu).to have_text(user.name) - end + expect(find_tribute_autocomplete_menu).to have_text(user_xss.username) + end - it 'selects the first item for non-assignee dropdowns if a query is entered' do - fill_in 'Comment', with: ':1' + it 'opens autocomplete menu for Milestone when field starts with text with item escaping HTML characters' do + milestone_xss_title = 'alert milestone <img src=x onerror="alert(\'Hello xss\');" a' + create(:milestone, project: project, title: milestone_xss_title) - wait_for_requests + fill_in 'Comment', with: '%' - expect(find_tribute_autocomplete_menu).to have_css('.highlight:first-of-type') - end + wait_for_requests - context 'when autocompleting for groups' do - it 'shows the group when searching for the name of the group' do - fill_in 'Comment', with: '@mygroup' + expect(find_tribute_autocomplete_menu).to have_text('alert milestone') + end - expect(find_tribute_autocomplete_menu).to have_text('My group') - end + it 'opens autocomplete menu for Labels when field starts with text with item escaping HTML characters' do + fill_in 'Comment', with: '~' - it 'does not show the group when searching for the name of the parent of the group' do - fill_in 'Comment', with: '@ancestor' + wait_for_requests - expect(find_tribute_autocomplete_menu).not_to have_text('My group') + expect(find_tribute_autocomplete_menu).to have_text('alert label') + end end - end - context 'if a selected value has special characters' do - it 'wraps the result in double quotes' do - fill_in 'Comment', with: "~#{label.title[0]}" + describe 'autocomplete highlighting' do + it 'auto-selects the first item with query', :aggregate_failures do + fill_in 'Comment', with: ':1' + wait_for_requests + expect(find_tribute_autocomplete_menu).to have_css('.highlight:first-of-type') - find_highlighted_tribute_autocomplete_menu.click - - expect(find_field('Comment').value).to have_text("~\"#{label.title}\"") + fill_in 'Comment', with: '@' + wait_for_requests + expect(find_tribute_autocomplete_menu).to have_css('.highlight:first-of-type') + end end - it "shows dropdown after a new line" do - fill_in 'Comment', with: "test\n\n@" - - expect(find_tribute_autocomplete_menu).to be_visible - end + describe 'assignees' do + it 'does not wrap with quotes for assignee values' do + fill_in 'Comment', with: "@#{user.username[0..2]}" - it 'doesn\'t wrap for assignee values' do - fill_in 'Comment', with: "@#{user.username[0..2]}" + find_highlighted_tribute_autocomplete_menu.click - find_highlighted_tribute_autocomplete_menu.click + expect(find_field('Comment').value).to have_text("@#{user.username}") + end - expect(find_field('Comment').value).to have_text("@#{user.username}") - end + it 'includes items for assignee dropdowns with non-ASCII characters in name' do + fill_in 'Comment', with: "@#{user.name[0...8]}" - it 'does not wrap for emoji values' do - fill_in 'Comment', with: ':cartwheel_' + wait_for_requests - find_highlighted_tribute_autocomplete_menu.click + expect(find_tribute_autocomplete_menu).to have_text(user.name) + end - expect(find_field('Comment').value).to have_text('cartwheel_tone1') - end + context 'when autocompleting for groups' do + it 'shows the group when searching for the name of the group' do + fill_in 'Comment', with: '@mygroup' - it 'does not open autocomplete if there is no space before' do - fill_in 'Comment', with: "hello:#{user.username[0..2]}" + expect(find_tribute_autocomplete_menu).to have_text('My group') + end - expect(page).not_to have_css('.tribute-container') - end + it 'does not show the group when searching for the name of the parent of the group' do + fill_in 'Comment', with: '@ancestor' - it 'autocompletes for quick actions' do - fill_in 'Comment', with: '/as' + expect(find_tribute_autocomplete_menu).not_to have_text('My group') + end + end - find_highlighted_tribute_autocomplete_menu.click + context 'when /assign quick action is selected' do + it 'lists users who are currently not assigned to the issue' do + note = find_field('Comment') + note.native.send_keys('/assign ') + # The `/assign` ajax response might replace the one by `@` below causing a failed test + # so we need to wait for the `/assign` ajax request to finish first + wait_for_requests + note.native.send_keys('@') + wait_for_requests + + expect(find_tribute_autocomplete_menu).not_to have_text(user.username) + expect(find_tribute_autocomplete_menu).to have_text(user2.username) + end - expect(find_field('Comment').value).to have_text('/assign') + it 'lists users who are currently not assigned to the issue when using /assign on the second line' do + note = find_field('Comment') + note.native.send_keys('/assign @user2') + note.native.send_keys(:enter) + note.native.send_keys('/assign ') + # The `/assign` ajax response might replace the one by `@` below causing a failed test + # so we need to wait for the `/assign` ajax request to finish first + wait_for_requests + note.native.send_keys('@') + wait_for_requests + + expect(find_tribute_autocomplete_menu).not_to have_text(user.username) + expect(find_tribute_autocomplete_menu).to have_text(user2.username) + end + end end - end - context 'assignees' do - let(:issue_assignee) { create(:issue, project: project) } - let(:unassigned_user) { create(:user) } + context 'if a selected value has special characters' do + it 'wraps the result in double quotes' do + fill_in 'Comment', with: "~#{label.title[0..2]}" - before do - issue_assignee.update(assignees: [user]) + find_highlighted_tribute_autocomplete_menu.click - project.add_maintainer(unassigned_user) - end + expect(find_field('Comment').value).to have_text("~\"#{label.title}\"") + end - it 'lists users who are currently not assigned to the issue when using /assign' do - visit project_issue_path(project, issue_assignee) + it 'does not wrap for emoji values' do + fill_in 'Comment', with: ':cartwheel_' - note = find_field('Comment') - note.native.send_keys('/assign ') - # The `/assign` ajax response might replace the one by `@` below causing a failed test - # so we need to wait for the `/assign` ajax request to finish first - wait_for_requests - note.native.send_keys('@') - wait_for_requests + find_highlighted_tribute_autocomplete_menu.click - expect(find_tribute_autocomplete_menu).not_to have_text(user.username) - expect(find_tribute_autocomplete_menu).to have_text(unassigned_user.username) + expect(find_field('Comment').value).to have_text('cartwheel_tone1') + end end - it 'lists users who are currently not assigned to the issue when using /assign on the second line' do - visit project_issue_path(project, issue_assignee) + context 'quick actions' do + it 'autocompletes for quick actions' do + fill_in 'Comment', with: '/as' - note = find_field('Comment') - note.native.send_keys('/assign @user2') - note.native.send_keys(:enter) - note.native.send_keys('/assign ') - # The `/assign` ajax response might replace the one by `@` below causing a failed test - # so we need to wait for the `/assign` ajax request to finish first - wait_for_requests - note.native.send_keys('@') - wait_for_requests + find_highlighted_tribute_autocomplete_menu.click - expect(find_tribute_autocomplete_menu).not_to have_text(user.username) - expect(find_tribute_autocomplete_menu).to have_text(unassigned_user.username) + expect(find_field('Comment').value).to have_text('/assign') + end end - end - context 'labels' do - it 'opens autocomplete menu for Labels when field starts with text with item escaping HTML characters' do - label_xss_title = 'alert label <img src=x onerror="alert(\'Hello xss\');" a' - create(:label, project: project, title: label_xss_title) + context 'labels' do + it 'allows colons when autocompleting scoped labels' do + fill_in 'Comment', with: '~scoped:' - fill_in 'Comment', with: '~' + wait_for_requests - wait_for_requests + expect(find_tribute_autocomplete_menu).to have_text('scoped::label') + end - expect(find_tribute_autocomplete_menu).to have_text('alert label') - end + it 'autocompletes multi-word labels' do + fill_in 'Comment', with: '~Acceptingmerge' - it 'allows colons when autocompleting scoped labels' do - create(:label, project: project, title: 'scoped:label') + wait_for_requests - fill_in 'Comment', with: '~scoped:' + expect(find_tribute_autocomplete_menu).to have_text('Accepting merge requests') + end - wait_for_requests + it 'only autocompletes the last label' do + fill_in 'Comment', with: '~scoped:: foo bar ~Acceptingmerge' + # Invoke autocompletion + find_field('Comment').native.send_keys(:right) - expect(find_tribute_autocomplete_menu).to have_text('scoped:label') - end + wait_for_requests - it 'allows colons when autocompleting scoped labels with double colons' do - create(:label, project: project, title: 'scoped::label') + expect(find_tribute_autocomplete_menu).to have_text('Accepting merge requests') + end - fill_in 'Comment', with: '~scoped::' + it 'does not autocomplete labels if no tilde is typed' do + fill_in 'Comment', with: 'Accepting' - wait_for_requests + wait_for_requests - expect(find_tribute_autocomplete_menu).to have_text('scoped::label') + expect(page).not_to have_css('.tribute-container') + end end - it 'autocompletes multi-word labels' do - create(:label, project: project, title: 'Accepting merge requests') + context 'when other notes are destroyed' do + let!(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) } - fill_in 'Comment', with: '~Acceptingmerge' + # This is meant to protect against this issue https://gitlab.com/gitlab-org/gitlab/-/issues/228729 + it 'keeps autocomplete key listeners' do + note = find_field('Comment') - wait_for_requests - - expect(find_tribute_autocomplete_menu).to have_text('Accepting merge requests') - end - - it 'only autocompletes the latest label' do - create(:label, project: project, title: 'documentation') - create(:label, project: project, title: 'feature') + start_comment_with_emoji(note, '.tribute-container li') - fill_in 'Comment', with: '~documentation foo bar ~feat' - # Invoke autocompletion - find_field('Comment').native.send_keys(:right) + start_and_cancel_discussion - wait_for_requests + note.fill_in(with: '') + start_comment_with_emoji(note, '.tribute-container li') + note.native.send_keys(:enter) - expect(find_tribute_autocomplete_menu).to have_text('feature') - expect(find_tribute_autocomplete_menu).not_to have_text('documentation') + expect(note.value).to eql('Hello :100: ') + end end - it 'does not autocomplete labels if no tilde is typed' do - create(:label, project: project, title: 'documentation') + shared_examples 'autocomplete suggestions' do + it 'suggests objects correctly' do + fill_in 'Comment', with: object.class.reference_prefix - fill_in 'Comment', with: 'document' - - wait_for_requests + find_tribute_autocomplete_menu.find('li').click - expect(page).not_to have_css('.tribute-container') + expect(find_field('Comment').value).to have_text(expected_body) + end end - end - - context 'when other notes are destroyed' do - let!(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) } - - # This is meant to protect against this issue https://gitlab.com/gitlab-org/gitlab/-/issues/228729 - it 'keeps autocomplete key listeners' do - visit project_issue_path(project, issue) - note = find_field('Comment') - - start_comment_with_emoji(note, '.tribute-container li') - - start_and_cancel_discussion - note.fill_in(with: '') - start_comment_with_emoji(note, '.tribute-container li') - note.native.send_keys(:enter) + context 'issues' do + let(:object) { issue } + let(:expected_body) { object.to_reference } - expect(note.value).to eql('Hello :100: ') + it_behaves_like 'autocomplete suggestions' end - end - - shared_examples 'autocomplete suggestions' do - it 'suggests objects correctly' do - fill_in 'Comment', with: object.class.reference_prefix - find_tribute_autocomplete_menu.find('li').click + context 'merge requests' do + let(:object) { create(:merge_request, source_project: project) } + let(:expected_body) { object.to_reference } - expect(find_field('Comment').value).to have_text(expected_body) + it_behaves_like 'autocomplete suggestions' end - end - - context 'issues' do - let(:object) { issue } - let(:expected_body) { object.to_reference } - it_behaves_like 'autocomplete suggestions' - end - - context 'merge requests' do - let(:object) { create(:merge_request, source_project: project) } - let(:expected_body) { object.to_reference } - - it_behaves_like 'autocomplete suggestions' - end + context 'project snippets' do + let!(:object) { snippet } + let(:expected_body) { object.to_reference } - context 'project snippets' do - let!(:object) { create(:project_snippet, project: project, title: 'code snippet') } - let(:expected_body) { object.to_reference } - - it_behaves_like 'autocomplete suggestions' - end - - context 'label' do - let!(:object) { label } - let(:expected_body) { object.title } - - it_behaves_like 'autocomplete suggestions' - end + it_behaves_like 'autocomplete suggestions' + end - context 'milestone' do - let!(:object) { create(:milestone, project: project) } - let(:expected_body) { object.to_reference } + context 'milestone' do + let!(:object) { create(:milestone, project: project) } + let(:expected_body) { object.to_reference } - it_behaves_like 'autocomplete suggestions' + it_behaves_like 'autocomplete suggestions' + end end end diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb index ca44978d223..04b4caa52fe 100644 --- a/spec/features/issues/issue_sidebar_spec.rb +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -30,79 +30,199 @@ RSpec.describe 'Issue Sidebar' do let(:user2) { create(:user) } let(:issue2) { create(:issue, project: project, author: user2) } - include_examples 'issuable invite members experiments' do - let(:issuable_path) { project_issue_path(project, issue2) } - end - - context 'when user is a developer' do + context 'when GraphQL assignees widget feature flag is disabled' do before do - project.add_developer(user) - visit_issue(project, issue2) - - find('.block.assignee .edit-link').click + stub_feature_flags(issue_assignees_widget: false) + end - wait_for_requests + include_examples 'issuable invite members experiments' do + let(:issuable_path) { project_issue_path(project, issue2) } end - it 'shows author in assignee dropdown' do - page.within '.dropdown-menu-user' do - expect(page).to have_content(user2.name) + context 'when user is a developer' do + before do + project.add_developer(user) + visit_issue(project, issue2) + + find('.block.assignee .edit-link').click + wait_for_requests + end + + it 'shows author in assignee dropdown' do + page.within '.dropdown-menu-user' do + expect(page).to have_content(user2.name) + end + end + + it 'shows author when filtering assignee dropdown' do + page.within '.dropdown-menu-user' do + find('.dropdown-input-field').set(user2.name) + + wait_for_requests + + expect(page).to have_content(user2.name) + end + end + + it 'assigns yourself' do + find('.block.assignee .dropdown-menu-toggle').click + + click_button 'assign yourself' + + wait_for_requests + + find('.block.assignee .edit-link').click + + page.within '.dropdown-menu-user' do + expect(page.find('.dropdown-header')).to be_visible + expect(page.find('.dropdown-menu-user-link.is-active')).to have_content(user.name) + end end - end - it 'shows author when filtering assignee dropdown' do - page.within '.dropdown-menu-user' do + it 'keeps your filtered term after filtering and dismissing the dropdown' do find('.dropdown-input-field').set(user2.name) wait_for_requests - expect(page).to have_content(user2.name) + page.within '.dropdown-menu-user' do + expect(page).not_to have_content 'Unassigned' + click_link user2.name + end + + find('.js-right-sidebar').click + find('.block.assignee .edit-link').click + + expect(page.all('.dropdown-menu-user li').length).to eq(1) + expect(find('.dropdown-input-field').value).to eq(user2.name) + end + + it 'shows label text as "Apply" when assignees are changed' do + project.add_developer(user) + visit_issue(project, issue2) + + find('.block.assignee .edit-link').click + wait_for_requests + + click_on 'Unassigned' + + expect(page).to have_link('Apply') end end + end - it 'assigns yourself' do - find('.block.assignee .dropdown-menu-toggle').click + context 'when GraphQL assignees widget feature flag is enabled' do + context 'when a privileged user can invite' do + it 'shows a link for inviting members and launches invite modal' do + project.add_maintainer(user) + visit_issue(project, issue2) - click_button 'assign yourself' + open_assignees_dropdown - wait_for_requests + page.within '.dropdown-menu-user' do + expect(page).to have_link('Invite members') + expect(page).to have_selector('[data-track-event="click_invite_members"]') + expect(page).to have_selector('[data-track-label="edit_assignee"]') + end - find('.block.assignee .edit-link').click + click_link 'Invite members' - page.within '.dropdown-menu-user' do - expect(page.find('.dropdown-header')).to be_visible - expect(page.find('.dropdown-menu-user-link.is-active')).to have_content(user.name) + expect(page).to have_content("You're inviting members to the") end end - it 'keeps your filtered term after filtering and dismissing the dropdown' do - find('.dropdown-input-field').set(user2.name) + context 'when invite_members_version_b experiment is enabled' do + before do + stub_experiment_for_subject(invite_members_version_b: true) + end + + it 'shows a link for inviting members and follows through to modal' do + project.add_developer(user) + visit_issue(project, issue2) - wait_for_requests + open_assignees_dropdown - page.within '.dropdown-menu-user' do - expect(page).not_to have_content 'Unassigned' - click_link user2.name + page.within '.dropdown-menu-user' do + expect(page).to have_link('Invite members', href: '#') + expect(page).to have_selector('[data-track-event="click_invite_members_version_b"]') + expect(page).to have_selector('[data-track-label="edit_assignee"]') + end + + click_link 'Invite members' + + expect(page).to have_content("Oops, this feature isn't ready yet") end + end + + context 'when invite_members_version_b experiment is disabled' do + it 'shows author in assignee dropdown and no invite link' do + project.add_developer(user) + visit_issue(project, issue2) - find('.js-right-sidebar').click - find('.block.assignee .edit-link').click + open_assignees_dropdown - expect(page.all('.dropdown-menu-user li').length).to eq(1) - expect(find('.dropdown-input-field').value).to eq(user2.name) + page.within '.dropdown-menu-user' do + expect(page).not_to have_link('Invite members') + end + end end - end - it 'shows label text as "Apply" when assignees are changed' do - project.add_developer(user) - visit_issue(project, issue2) + context 'when user is a developer' do + before do + project.add_developer(user) + visit_issue(project, issue2) + end + + it 'shows author in assignee dropdown' do + open_assignees_dropdown + + page.within '.dropdown-menu-user' do + expect(page).to have_content(user2.name) + end + end + + it 'shows author when filtering assignee dropdown' do + open_assignees_dropdown + + page.within '.dropdown-menu-user' do + find('.js-dropdown-input-field').find('input').set(user2.name) + + wait_for_requests + + expect(page).to have_content(user2.name) + end + end + + it 'assigns yourself' do + click_button 'assign yourself' + wait_for_requests + + page.within '.assignee' do + expect(page).to have_content(user.name) + end + end - find('.block.assignee .edit-link').click - wait_for_requests + it 'keeps your filtered term after filtering and dismissing the dropdown' do + open_assignees_dropdown - click_on 'Unassigned' + find('.js-dropdown-input-field').find('input').set(user2.name) + wait_for_requests + + page.within '.dropdown-menu-user' do + expect(page).not_to have_content 'Unassigned' + click_link user2.name + end - expect(page).to have_link('Apply') + find('.js-right-sidebar').click + + open_assignees_dropdown + + page.within('.assignee') do + expect(page.all('[data-testid="selected-participant"]').length).to eq(1) + end + + expect(find('.js-dropdown-input-field').find('input').value).to eq(user2.name) + end + end end end @@ -171,7 +291,7 @@ RSpec.describe 'Issue Sidebar' do context 'editing issue labels', :js do before do - issue.update(labels: [label]) + issue.update!(labels: [label]) page.within('.block.labels') do click_on 'Edit' end @@ -334,4 +454,11 @@ RSpec.describe 'Issue Sidebar' do find('aside.right-sidebar.right-sidebar-collapsed .js-sidebar-toggle').click find('aside.right-sidebar.right-sidebar-expanded') end + + def open_assignees_dropdown + page.within('.assignee') do + click_button('Edit') + wait_for_requests + end + end end diff --git a/spec/features/issues/markdown_toolbar_spec.rb b/spec/features/issues/markdown_toolbar_spec.rb index 6dc1cbfb2d7..aad5d319bc4 100644 --- a/spec/features/issues/markdown_toolbar_spec.rb +++ b/spec/features/issues/markdown_toolbar_spec.rb @@ -3,9 +3,9 @@ require 'spec_helper' RSpec.describe 'Issue markdown toolbar', :js do - let(:project) { create(:project, :public) } - let(:issue) { create(:issue, project: project) } - let(:user) { create(:user) } + let_it_be(:project) { create(:project, :public) } + let_it_be(:issue) { create(:issue, project: project) } + let_it_be(:user) { create(:user) } before do sign_in(user) @@ -14,28 +14,22 @@ RSpec.describe 'Issue markdown toolbar', :js do end it "doesn't include first new line when adding bold" do - find('#note-body').native.send_keys('test') - find('#note-body').native.send_key(:enter) - find('#note-body').native.send_keys('bold') + fill_in 'Comment', with: "test\nbold" - find('.js-main-target-form #note-body') - page.evaluate_script('document.querySelectorAll(".js-main-target-form #note-body")[0].setSelectionRange(4, 9)') + page.evaluate_script('document.getElementById("note-body").setSelectionRange(4, 9)') - first('.toolbar-btn').click + click_button 'Add bold text' - expect(find('#note-body')[:value]).to eq("test\n**bold**\n") + expect(find_field('Comment').value).to eq("test\n**bold**\n") end it "doesn't include first new line when adding underline" do - find('#note-body').native.send_keys('test') - find('#note-body').native.send_key(:enter) - find('#note-body').native.send_keys('underline') + fill_in 'Comment', with: "test\nunderline" - find('.js-main-target-form #note-body') - page.evaluate_script('document.querySelectorAll(".js-main-target-form #note-body")[0].setSelectionRange(4, 50)') + page.evaluate_script('document.getElementById("note-body").setSelectionRange(4, 50)') - all('.toolbar-btn')[1].click + click_button 'Add italic text' - expect(find('#note-body')[:value]).to eq("test\n_underline_\n") + expect(find_field('Comment').value).to eq("test\n_underline_\n") end end diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb index bc4c67fdd79..5e02d5ad038 100644 --- a/spec/features/issues/note_polling_spec.rb +++ b/spec/features/issues/note_polling_spec.rb @@ -103,7 +103,7 @@ RSpec.describe 'Issue notes polling', :js do end def update_note(note, new_text) - note.update(note: new_text) + note.update!(note: new_text) wait_for_requests end diff --git a/spec/features/issues/spam_issues_spec.rb b/spec/features/issues/spam_issues_spec.rb index aec806c566d..461030d3176 100644 --- a/spec/features/issues/spam_issues_spec.rb +++ b/spec/features/issues/spam_issues_spec.rb @@ -43,7 +43,7 @@ RSpec.describe 'New issue', :js do end it 'rejects issue creation' do - click_button 'Submit issue' + click_button 'Create issue' expect(page).to have_content('discarded') expect(page).not_to have_content('potential spam') @@ -51,7 +51,7 @@ RSpec.describe 'New issue', :js do end it 'creates a spam log record' do - expect { click_button 'Submit issue' } + expect { click_button 'Create issue' } .to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue') end end @@ -63,14 +63,14 @@ RSpec.describe 'New issue', :js do end it 'allows issue creation' do - click_button 'Submit issue' + click_button 'Create issue' expect(page.find('.issue-details h2.title')).to have_content('issue title') expect(page.find('.issue-details .description')).to have_content('issue description') end it 'creates a spam log record' do - expect { click_button 'Submit issue' } + expect { click_button 'Create issue' } .to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue') end end @@ -101,14 +101,14 @@ RSpec.describe 'New issue', :js do fill_in 'issue_title', with: 'issue title' fill_in 'issue_description', with: 'issue description' - click_button 'Submit issue' + click_button 'Create issue' # it is impossible to test reCAPTCHA automatically and there is no possibility to fill in recaptcha # reCAPTCHA verification is skipped in test environment and it always returns true expect(page).not_to have_content('issue title') expect(page).to have_css('.recaptcha') - click_button 'Submit issue' + click_button 'Create issue' expect(page.find('.issue-details h2.title')).to have_content('issue title') expect(page.find('.issue-details .description')).to have_content('issue description') @@ -122,7 +122,7 @@ RSpec.describe 'New issue', :js do end it 'creates an issue without a need to solve reCAPTCHA' do - click_button 'Submit issue' + click_button 'Create issue' expect(page).not_to have_css('.recaptcha') expect(page.find('.issue-details h2.title')).to have_content('issue title') @@ -130,7 +130,7 @@ RSpec.describe 'New issue', :js do end it 'creates a spam log record' do - expect { click_button 'Submit issue' } + expect { click_button 'Create issue' } .to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue') end end @@ -148,7 +148,7 @@ RSpec.describe 'New issue', :js do end it 'creates an issue without a need to solve reCaptcha' do - click_button 'Submit issue' + click_button 'Create issue' expect(page).not_to have_css('.recaptcha') expect(page.find('.issue-details h2.title')).to have_content('issue title') @@ -156,7 +156,7 @@ RSpec.describe 'New issue', :js do end it 'creates a spam log record' do - expect { click_button 'Submit issue' } + expect { click_button 'Create issue' } .to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue') end end @@ -178,7 +178,7 @@ RSpec.describe 'New issue', :js do fill_in 'issue_title', with: 'issue title' fill_in 'issue_description', with: 'issue description' - click_button 'Submit issue' + click_button 'Create issue' expect(page.find('.issue-details h2.title')).to have_content('issue title') expect(page.find('.issue-details .description')).to have_content('issue description') diff --git a/spec/features/issues/user_comments_on_issue_spec.rb b/spec/features/issues/user_comments_on_issue_spec.rb index 004488f2f64..09d3ad15641 100644 --- a/spec/features/issues/user_comments_on_issue_spec.rb +++ b/spec/features/issues/user_comments_on_issue_spec.rb @@ -57,17 +57,9 @@ RSpec.describe "User comments on issue", :js do project.add_maintainer(user) create(:label, project: project, title: 'label') - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('/l') - end - - wait_for_requests - - expect(page).to have_selector('.atwho-container') + fill_in 'Comment', with: '/l' - page.within '.atwho-container #at-view-commands' do - expect(find('li', match: :first)).to have_content('/label') - end + expect(find_highlighted_autocomplete_item).to have_content('/label') end end @@ -110,4 +102,10 @@ RSpec.describe "User comments on issue", :js do end end end + + private + + def find_highlighted_autocomplete_item + find('.atwho-view li.cur', visible: true) + end end 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 e225a45481d..6e8b3e4fb7c 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 @@ -150,7 +150,7 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do context 'when merge requests are disabled' do before do - project.project_feature.update(merge_requests_access_level: 0) + project.project_feature.update!(merge_requests_access_level: 0) visit project_issue_path(project, issue) end diff --git a/spec/features/issues/user_creates_confidential_merge_request_spec.rb b/spec/features/issues/user_creates_confidential_merge_request_spec.rb index ea96165d7b7..6b4526cd624 100644 --- a/spec/features/issues/user_creates_confidential_merge_request_spec.rb +++ b/spec/features/issues/user_creates_confidential_merge_request_spec.rb @@ -38,7 +38,7 @@ RSpec.describe 'User creates confidential merge request on issue page', :js do let(:forked_project) { fork_project(project, user, repository: true) } before do - forked_project.update(visibility: Gitlab::VisibilityLevel::PRIVATE) + forked_project.update!(visibility: Gitlab::VisibilityLevel::PRIVATE) visit_confidential_issue end diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb index 98f9ed6c6a2..e2e204f03db 100644 --- a/spec/features/issues/user_creates_issue_spec.rb +++ b/spec/features/issues/user_creates_issue_spec.rb @@ -45,7 +45,7 @@ RSpec.describe "User creates issue" do .and have_no_content("Milestone") expect(page.find('#issue_title')['placeholder']).to eq 'Title' - expect(page.find('#issue_description')['placeholder']).to eq 'Write a comment or drag your files here…' + expect(page.find('#issue_description')['placeholder']).to eq 'Write a description or drag your files here…' end issue_title = "500 error on profile" @@ -54,7 +54,7 @@ RSpec.describe "User creates issue" do first('.js-md').click first('.rspec-issuable-form-description').native.send_keys('Description') - click_button("Submit issue") + click_button("Create issue") expect(page).to have_content(issue_title) .and have_content(user.name) @@ -112,7 +112,7 @@ RSpec.describe "User creates issue" do fill_in("Title", with: issue_title) click_button("Label") click_link(label_titles.first) - click_button("Submit issue") + click_button("Create issue") expect(page).to have_content(issue_title) .and have_content(user.name) @@ -135,7 +135,7 @@ RSpec.describe "User creates issue" do expect(find('#issuable-due-date').value).to eq date.to_s - click_button 'Submit issue' + click_button 'Create issue' page.within '.issuable-sidebar' do expect(page).to have_content date.to_s(:medium) @@ -259,7 +259,7 @@ RSpec.describe "User creates issue" do fill_in 'issue_title', with: 'bug 345' fill_in 'issue_description', with: 'bug description' - click_button 'Submit issue' + click_button 'Create issue' end end end diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb index 9d4a6cdb522..1bbb96ff479 100644 --- a/spec/features/issues/user_edits_issue_spec.rb +++ b/spec/features/issues/user_edits_issue_spec.rb @@ -78,7 +78,7 @@ RSpec.describe "Issues > User edits issue", :js do end it 'warns about version conflict' do - issue.update(title: "New title") + issue.update!(title: "New title") fill_in 'issue_title', with: 'bug 345' fill_in 'issue_description', with: 'bug description' @@ -142,10 +142,8 @@ RSpec.describe "Issues > User edits issue", :js do it 'can remove label without removing label added via quick action', :aggregate_failures do # Add `syzygy` label with a quick action - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys('/label ~syzygy') - end + fill_in 'Comment', with: '/label ~syzygy' + click_button 'Comment' wait_for_requests @@ -169,80 +167,165 @@ RSpec.describe "Issues > User edits issue", :js do end describe 'update assignee' do - context 'by authorized user' do - def close_dropdown_menu_if_visible - find('.dropdown-menu-toggle', visible: :all).tap do |toggle| - toggle.click if toggle.visible? - end + context 'when GraphQL assignees widget feature flag is disabled' do + before do + stub_feature_flags(issue_assignees_widget: false) end - it 'allows user to select unassigned' do - visit project_issue_path(project, issue) + context 'by authorized user' do + def close_dropdown_menu_if_visible + find('.dropdown-menu-toggle', visible: :all).tap do |toggle| + toggle.click if toggle.visible? + end + end - page.within('.assignee') do - expect(page).to have_content "#{user.name}" + it 'allows user to select unassigned' do + visit project_issue_path(project, issue) - click_link 'Edit' - click_link 'Unassigned' - first('.title').click - expect(page).to have_content 'None - assign yourself' + page.within('.assignee') do + expect(page).to have_content "#{user.name}" + + click_link 'Edit' + click_link 'Unassigned' + first('.title').click + + expect(page).to have_content 'None - assign yourself' + end end - end - it 'allows user to select an assignee' do - issue2 = create(:issue, project: project, author: user) - visit project_issue_path(project, issue2) + it 'allows user to select an assignee' do + issue2 = create(:issue, project: project, author: user) + visit project_issue_path(project, issue2) - page.within('.assignee') do - expect(page).to have_content "None" + page.within('.assignee') do + expect(page).to have_content "None" + end + + page.within '.assignee' do + click_link 'Edit' + end + + page.within '.dropdown-menu-user' do + click_link user.name + end + + page.within('.assignee') do + expect(page).to have_content user.name + end end - page.within '.assignee' do - click_link 'Edit' + it 'allows user to unselect themselves' do + issue2 = create(:issue, project: project, author: user, assignees: [user]) + + visit project_issue_path(project, issue2) + + page.within '.assignee' do + expect(page).to have_content user.name + + click_link 'Edit' + click_link user.name + + close_dropdown_menu_if_visible + + page.within '.value .assign-yourself' do + expect(page).to have_content "None" + end + end end + end + + context 'by unauthorized user' do + let(:guest) { create(:user) } - page.within '.dropdown-menu-user' do - click_link user.name + before do + project.add_guest(guest) end - page.within('.assignee') do - expect(page).to have_content user.name + it 'shows assignee text' do + sign_out(:user) + sign_in(guest) + + visit project_issue_path(project, issue) + expect(page).to have_content issue.assignees.first.name end end + end - it 'allows user to unselect themselves' do - issue2 = create(:issue, project: project, author: user, assignees: [user]) + context 'when GraphQL assignees widget feature flag is enabled' do + context 'by authorized user' do + it 'allows user to select unassigned' do + visit project_issue_path(project, issue) - visit project_issue_path(project, issue2) + page.within('.assignee') do + expect(page).to have_content "#{user.name}" - page.within '.assignee' do - expect(page).to have_content user.name + click_button('Edit') + wait_for_requests - click_link 'Edit' - click_link user.name + find('[data-testid="unassign"]').click + find('[data-testid="title"]').click + wait_for_requests + + expect(page).to have_content 'None - assign yourself' + end + end - close_dropdown_menu_if_visible + it 'allows user to select an assignee' do + issue2 = create(:issue, project: project, author: user) + visit project_issue_path(project, issue2) - page.within '.value .assign-yourself' do + page.within('.assignee') do expect(page).to have_content "None" + click_button('Edit') + wait_for_requests + end + + page.within '.dropdown-menu-user' do + click_link user.name + end + + page.within('.assignee') do + find('[data-testid="title"]').click + wait_for_requests + + expect(page).to have_content user.name end end - end - end - context 'by unauthorized user' do - let(:guest) { create(:user) } + it 'allows user to unselect themselves' do + issue2 = create(:issue, project: project, author: user, assignees: [user]) - before do - project.add_guest(guest) + visit project_issue_path(project, issue2) + + page.within '.assignee' do + expect(page).to have_content user.name + + click_button('Edit') + wait_for_requests + click_link user.name + + find('[data-testid="title"]').click + wait_for_requests + + expect(page).to have_content "None" + end + end end - it 'shows assignee text' do - sign_out(:user) - sign_in(guest) + context 'by unauthorized user' do + let(:guest) { create(:user) } - visit project_issue_path(project, issue) - expect(page).to have_content issue.assignees.first.name + before do + project.add_guest(guest) + end + + it 'shows assignee text' do + sign_out(:user) + sign_in(guest) + + visit project_issue_path(project, issue) + expect(page).to have_content issue.assignees.first.name + end end end end @@ -309,7 +392,7 @@ RSpec.describe "Issues > User edits issue", :js do before do project.add_guest(guest) issue.milestone = milestone - issue.save + issue.save! end it 'shows milestone text' do @@ -326,24 +409,23 @@ RSpec.describe "Issues > User edits issue", :js do it 'adds due date to issue' do date = Date.today.at_beginning_of_month + 2.days - page.within '.due_date' do - click_link 'Edit' - + page.within '[data-testid="due-date"]' do + click_button 'Edit' page.within '.pika-single' do click_button date.day end wait_for_requests - expect(find('.value').text).to have_content date.strftime('%b %-d, %Y') + expect(find('[data-testid="sidebar-duedate-value"]').text).to have_content date.strftime('%b %-d, %Y') end end it 'removes due date from issue' do date = Date.today.at_beginning_of_month + 2.days - page.within '.due_date' do - click_link 'Edit' + page.within '[data-testid="due-date"]' do + click_button 'Edit' page.within '.pika-single' do click_button date.day @@ -353,7 +435,7 @@ RSpec.describe "Issues > User edits issue", :js do expect(page).to have_no_content 'None' - click_link 'remove due date' + click_button 'remove due date' expect(page).to have_content 'None' end end diff --git a/spec/features/issues/user_filters_issues_spec.rb b/spec/features/issues/user_filters_issues_spec.rb index 1b246181523..5d05df6aaf0 100644 --- a/spec/features/issues/user_filters_issues_spec.rb +++ b/spec/features/issues/user_filters_issues_spec.rb @@ -18,7 +18,7 @@ RSpec.describe 'User filters issues', :js do @issue = Issue.find_by(title: 'foobar') @issue.milestone = create(:milestone, project: project) @issue.assignees = [] - @issue.save + @issue.save! end let(:issue) { @issue } diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb index 1c7bc5f239f..e862f7030c0 100644 --- a/spec/features/issues/user_interacts_with_awards_spec.rb +++ b/spec/features/issues/user_interacts_with_awards_spec.rb @@ -5,6 +5,10 @@ require 'spec_helper' RSpec.describe 'User interacts with awards' do let(:user) { create(:user) } + before do + stub_feature_flags(improved_emoji_picker: false) + end + describe 'User interacts with awards in an issue', :js do let(:issue) { create(:issue, project: project)} let(:project) { create(:project) } diff --git a/spec/features/issues/user_invites_from_a_comment_spec.rb b/spec/features/issues/user_invites_from_a_comment_spec.rb new file mode 100644 index 00000000000..82061f6ed79 --- /dev/null +++ b/spec/features/issues/user_invites_from_a_comment_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe "User invites from a comment", :js do + let_it_be(:project) { create(:project_empty_repo, :public) } + let_it_be(:issue) { create(:issue, project: project) } + let_it_be(:user) { project.owner } + + before do + sign_in(user) + end + + it "launches the invite modal from invite link on a comment" do + stub_experiments(invite_members_in_comment: :invite_member_link) + + visit project_issue_path(project, issue) + + page.within(".new-note") do + click_button 'Invite Member' + end + + expect(page).to have_content("You're inviting members to the") + end +end diff --git a/spec/features/issues/user_sees_live_update_spec.rb b/spec/features/issues/user_sees_live_update_spec.rb index 79c6978cbc0..7e4880f209e 100644 --- a/spec/features/issues/user_sees_live_update_spec.rb +++ b/spec/features/issues/user_sees_live_update_spec.rb @@ -18,7 +18,7 @@ RSpec.describe 'Issues > User sees live update', :js do expect(page).to have_text("new title") - issue.update(title: "updated title") + issue.update!(title: "updated title") wait_for_requests expect(page).to have_text("updated title") diff --git a/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb b/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb index 7a2b637e48e..6473fe01052 100644 --- a/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb +++ b/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb @@ -19,11 +19,14 @@ RSpec.describe 'Issues > Real-time sidebar', :js do expect(page.find('.assignee')).to have_content 'None' end - gitlab_sign_in(user) + sign_in(user) + visit project_issue_path(project, issue) expect(page.find('.assignee')).to have_content 'None' click_button 'assign yourself' + wait_for_requests + expect(page.find('.assignee')).to have_content user.name using_session :other_session do expect(page.find('.assignee')).to have_content user.name diff --git a/spec/features/issues/user_sorts_issues_spec.rb b/spec/features/issues/user_sorts_issues_spec.rb index f0bb055c6f2..c161e1deb83 100644 --- a/spec/features/issues/user_sorts_issues_spec.rb +++ b/spec/features/issues/user_sorts_issues_spec.rb @@ -77,7 +77,7 @@ RSpec.describe "User sorts issues" do it 'sorts by most recently updated', :js do issue3.updated_at = Time.now + 100 - issue3.save + issue3.save! visit project_issues_path(project, sort: sort_value_recently_updated) expect(first_issue).to include('baz') @@ -85,8 +85,8 @@ RSpec.describe "User sorts issues" do describe 'sorting by due date', :js do before do - issue1.update(due_date: 1.day.from_now) - issue2.update(due_date: 6.days.from_now) + issue1.update!(due_date: 1.day.from_now) + issue2.update!(due_date: 6.days.from_now) end it 'sorts by due date' do @@ -96,7 +96,7 @@ RSpec.describe "User sorts issues" do end it 'sorts by due date by excluding nil due dates' do - issue2.update(due_date: nil) + issue2.update!(due_date: nil) visit project_issues_path(project, sort: sort_value_due_date) @@ -111,7 +111,7 @@ RSpec.describe "User sorts issues" do end it 'sorts by least recently due date by excluding nil due dates' do - issue2.update(due_date: nil) + issue2.update!(due_date: nil) visit project_issues_path(project, label_names: [label.name], sort: sort_value_due_date_later) @@ -122,8 +122,8 @@ RSpec.describe "User sorts issues" do describe 'filtering by due date', :js do before do - issue1.update(due_date: 1.day.from_now) - issue2.update(due_date: 6.days.from_now) + issue1.update!(due_date: 1.day.from_now) + issue2.update!(due_date: 6.days.from_now) end it 'filters by none' do @@ -147,9 +147,9 @@ RSpec.describe "User sorts issues" do end it 'filters by due this week' do - issue1.update(due_date: Date.today.beginning_of_week + 2.days) - issue2.update(due_date: Date.today.end_of_week) - issue3.update(due_date: Date.today - 8.days) + issue1.update!(due_date: Date.today.beginning_of_week + 2.days) + issue2.update!(due_date: Date.today.end_of_week) + issue3.update!(due_date: Date.today - 8.days) visit project_issues_path(project, due_date: Issue::DueThisWeek.name) @@ -161,9 +161,9 @@ RSpec.describe "User sorts issues" do end it 'filters by due this month' do - issue1.update(due_date: Date.today.beginning_of_month + 2.days) - issue2.update(due_date: Date.today.end_of_month) - issue3.update(due_date: Date.today - 50.days) + issue1.update!(due_date: Date.today.beginning_of_month + 2.days) + issue2.update!(due_date: Date.today.end_of_month) + issue3.update!(due_date: Date.today - 50.days) visit project_issues_path(project, due_date: Issue::DueThisMonth.name) @@ -175,9 +175,9 @@ RSpec.describe "User sorts issues" do end it 'filters by overdue' do - issue1.update(due_date: Date.today + 2.days) - issue2.update(due_date: Date.today + 20.days) - issue3.update(due_date: Date.yesterday) + issue1.update!(due_date: Date.today + 2.days) + issue2.update!(due_date: Date.today + 20.days) + issue3.update!(due_date: Date.yesterday) visit project_issues_path(project, due_date: Issue::Overdue.name) @@ -189,9 +189,9 @@ RSpec.describe "User sorts issues" do end it 'filters by due next month and previous two weeks' do - issue1.update(due_date: Date.today - 4.weeks) - issue2.update(due_date: (Date.today + 2.months).beginning_of_month) - issue3.update(due_date: Date.yesterday) + issue1.update!(due_date: Date.today - 4.weeks) + issue2.update!(due_date: (Date.today + 2.months).beginning_of_month) + issue3.update!(due_date: Date.yesterday) visit project_issues_path(project, due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name) @@ -206,9 +206,9 @@ RSpec.describe "User sorts issues" do describe 'sorting by milestone', :js do before do issue1.milestone = newer_due_milestone - issue1.save + issue1.save! issue2.milestone = later_due_milestone - issue2.save + issue2.save! end it 'sorts by milestone' do @@ -224,9 +224,9 @@ RSpec.describe "User sorts issues" do before do issue1.assignees << user2 - issue1.save + issue1.save! issue2.assignees << user2 - issue2.save + issue2.save! end it 'sorts with a filter applied' do diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb index e84b300a748..3208ad82c03 100644 --- a/spec/features/markdown/markdown_spec.rb +++ b/spec/features/markdown/markdown_spec.rb @@ -63,8 +63,8 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do end aggregate_failures 'parses fenced code blocks' do - expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.c') - expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.python') + expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.language-c') + expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.language-python') end aggregate_failures 'parses mermaid code block' do @@ -288,9 +288,10 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do @wiki = @feat.wiki @wiki_page = @feat.wiki_page - path = 'images/example.jpg' - gitaly_wiki_file = Gitlab::GitalyClient::WikiFile.new(path: path) - expect(@wiki).to receive(:find_file).with(path, load_content: false).and_return(Gitlab::Git::WikiFile.new(gitaly_wiki_file)) + name = 'example.jpg' + path = "images/#{name}" + blob = double(name: name, path: path, mime_type: 'image/jpeg', data: nil) + expect(@wiki).to receive(:find_file).with(path, load_content: false).and_return(Gitlab::Git::WikiFile.new(blob)) allow(@wiki).to receive(:wiki_base_path) { '/namespace1/gitlabhq/wikis' } @html = markdown(@feat.raw_markdown, { pipeline: :wiki, wiki: @wiki, page_slug: @wiki_page.slug }) diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb index 25f2707146d..19680a827bf 100644 --- a/spec/features/merge_request/batch_comments_spec.rb +++ b/spec/features/merge_request/batch_comments_spec.rb @@ -28,7 +28,7 @@ RSpec.describe 'Merge request > Batch comments', :js do end it 'adds draft note' do - write_comment + write_diff_comment expect(find('.draft-note-component')).to have_content('Line is wrong') @@ -38,7 +38,7 @@ RSpec.describe 'Merge request > Batch comments', :js do end it 'publishes review' do - write_comment + write_diff_comment page.within('.review-bar-content') do click_button 'Submit review' @@ -52,7 +52,7 @@ RSpec.describe 'Merge request > Batch comments', :js do end it 'publishes single comment' do - write_comment + write_diff_comment click_button 'Add comment now' @@ -64,7 +64,7 @@ RSpec.describe 'Merge request > Batch comments', :js do end it 'deletes draft note' do - write_comment + write_diff_comment accept_alert { find('.js-note-delete').click } @@ -74,21 +74,70 @@ RSpec.describe 'Merge request > Batch comments', :js do end it 'edits draft note' do - write_comment + write_diff_comment find('.js-note-edit').click # make sure comment form is in view execute_script("window.scrollBy(0, 200)") - page.within('.js-discussion-note-form') do - fill_in('note_note', with: 'Testing update') - click_button('Save comment') + write_comment(text: 'Testing update', button_text: 'Save comment') + + expect(page).to have_selector('.draft-note-component', text: 'Testing update') + end + + context 'with image and file draft note' do + let(:merge_request) { create(:merge_request_with_diffs, :with_image_diffs, source_project: project) } + let!(:draft_on_text) { create(:draft_note_on_text_diff, merge_request: merge_request, author: user, path: 'README.md', note: 'Lorem ipsum on text...') } + let!(:draft_on_image) { create(:draft_note_on_image_diff, merge_request: merge_request, author: user, path: 'files/images/ee_repo_logo.png', note: 'Lorem ipsum on an image...') } + + it 'does not show in overview' do + visit_overview + + expect(page).to have_no_text(draft_on_text.note) + expect(page).to have_no_text(draft_on_image.note) end + end - wait_for_requests + context 'adding single comment to review' do + before do + visit_overview + end - expect(page).to have_selector('.draft-note-component', text: 'Testing update') + it 'at first does not show `Add to review` and `Add comment now` buttons' do + expect(page).to have_no_button('Add to review') + expect(page).to have_no_button('Add comment now') + end + + context 'when review has started' do + before do + visit_diffs + + write_diff_comment + + visit_overview + end + + it 'can add comment to review' do + write_comment(selector: '.js-main-target-form', field: 'note-body', text: 'Its a draft comment', button_text: 'Add to review') + + expect(page).to have_selector('.draft-note-component', text: 'Its a draft comment') + + click_button('Pending comments') + + expect(page).to have_text('2 pending comments') + end + + it 'can add comment right away' do + write_comment(selector: '.js-main-target-form', field: 'note-body', text: 'Its a regular comment', button_text: 'Add comment now') + + expect(page).to have_selector('.note:not(.draft-note)', text: 'Its a regular comment') + + click_button('Pending comments') + + expect(page).to have_text('1 pending comment') + end + end end context 'in parallel diff' do @@ -197,46 +246,51 @@ RSpec.describe 'Merge request > Batch comments', :js do wait_for_requests end - def write_comment(button_text: 'Start a review', text: 'Line is wrong') - click_diff_line(find("[id='#{sample_compare.changes[0][:line_code]}']")) - - page.within('.js-discussion-note-form') do - fill_in('note_note', with: text) - click_button(button_text) - end + def visit_overview + visit project_merge_request_path(merge_request.project, merge_request) wait_for_requests end - def write_parallel_comment(line, button_text: 'Start a review', text: 'Line is wrong') + def write_diff_comment(**params) + click_diff_line(find("[id='#{sample_compare.changes[0][:line_code]}']")) + + write_comment(**params) + end + + def write_parallel_comment(line, **params) find("td[id='#{line}']").hover find(".is-over button").click - page.within("form[data-line-code='#{line}']") do - fill_in('note_note', with: text) + write_comment(selector: "form[data-line-code='#{line}']", **params) + end + + def write_comment(selector: '.js-discussion-note-form', field: 'note_note', button_text: 'Start a review', text: 'Line is wrong') + page.within(selector) do + fill_in(field, with: text) click_button(button_text) end wait_for_requests end -end -def write_reply_to_discussion(button_text: 'Start a review', text: 'Line is wrong', resolve: false, unresolve: false) - page.within(first('.diff-files-holder .discussion-reply-holder')) do - find_field('Reply…', match: :first).click + def write_reply_to_discussion(button_text: 'Start a review', text: 'Line is wrong', resolve: false, unresolve: false) + page.within(first('.diff-files-holder .discussion-reply-holder')) do + find_field('Reply…', match: :first).click - fill_in('note_note', with: text) + fill_in('note_note', with: text) - if resolve - page.check('Resolve thread') - end + if resolve + page.check('Resolve thread') + end + + if unresolve + page.check('Unresolve thread') + end - if unresolve - page.check('Unresolve thread') + click_button(button_text) end - click_button(button_text) + wait_for_requests end - - wait_for_requests end diff --git a/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb b/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb index fd13083c185..d36abf86518 100644 --- a/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb +++ b/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb @@ -28,7 +28,7 @@ RSpec.describe 'create a merge request, allowing commits from members who can me check 'Allow commits from members who can merge to the target branch' - click_button 'Submit merge request' + click_button 'Create merge request' wait_for_requests diff --git a/spec/features/merge_request/user_awards_emoji_spec.rb b/spec/features/merge_request/user_awards_emoji_spec.rb index 62e4209f386..6f46cc20cba 100644 --- a/spec/features/merge_request/user_awards_emoji_spec.rb +++ b/spec/features/merge_request/user_awards_emoji_spec.rb @@ -17,33 +17,28 @@ RSpec.describe 'Merge request > User awards emoji', :js do end it 'adds award to merge request' do - first('.js-emoji-btn').click - expect(page).to have_selector('.js-emoji-btn.active') - expect(first('.js-emoji-btn')).to have_content '1' + first('[data-testid="award-button"]').click + expect(page).to have_selector('[data-testid="award-button"].is-active') + expect(first('[data-testid="award-button"]')).to have_content '1' visit project_merge_request_path(project, merge_request) - expect(first('.js-emoji-btn')).to have_content '1' + expect(first('[data-testid="award-button"]')).to have_content '1' end it 'removes award from merge request' do - first('.js-emoji-btn').click - find('.js-emoji-btn.active').click - expect(first('.js-emoji-btn')).to have_content '0' + first('[data-testid="award-button"]').click + find('[data-testid="award-button"].is-active').click + expect(first('[data-testid="award-button"]')).to have_content '0' visit project_merge_request_path(project, merge_request) - expect(first('.js-emoji-btn')).to have_content '0' - end - - it 'has only one menu on the page' do - first('.js-add-award').click - expect(page).to have_selector('.emoji-menu') - - expect(page).to have_selector('.emoji-menu', count: 1) + expect(first('[data-testid="award-button"]')).to have_content '0' end it 'adds awards to note' do - first('.js-note-emoji').click - first('.emoji-menu .js-emoji-btn').click + page.within('.note-actions') do + first('.note-emoji-button').click + find('gl-emoji[data-name="8ball"]').click + end wait_for_requests 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 37d329d4d5d..119cf31098c 100644 --- a/spec/features/merge_request/user_creates_merge_request_spec.rb +++ b/spec/features/merge_request/user_creates_merge_request_spec.rb @@ -31,7 +31,7 @@ RSpec.describe "User creates a merge request", :js do end fill_in("Title", with: title) - click_button("Submit merge request") + click_button("Create merge request") page.within(".merge-request") do expect(page).to have_content(title) @@ -87,7 +87,7 @@ RSpec.describe "User creates a merge request", :js do click_button("Compare branches and continue") - expect(page).to have_css("h3.page-title", text: "New Merge Request") + expect(page).to have_css("h3.page-title", text: "New merge request") page.within("form#new_merge_request") do fill_in("Title", with: title) @@ -103,7 +103,7 @@ RSpec.describe "User creates a merge request", :js do end find('.js-assignee-search').click - click_button("Submit merge request") + click_button("Create merge request") expect(page).to have_content(title).and have_content("Request to merge #{user.namespace.path}:#{source_branch} into master") end diff --git a/spec/features/merge_request/user_invites_from_a_comment_spec.rb b/spec/features/merge_request/user_invites_from_a_comment_spec.rb new file mode 100644 index 00000000000..79865094fd0 --- /dev/null +++ b/spec/features/merge_request/user_invites_from_a_comment_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe "User invites from a comment", :js do + let_it_be(:project) { create(:project, :public, :repository) } + let_it_be(:merge_request) { create(:merge_request, source_project: project) } + let_it_be(:user) { project.owner } + + before do + sign_in(user) + end + + it "launches the invite modal from invite link on a comment" do + stub_experiments(invite_members_in_comment: :invite_member_link) + + visit project_merge_request_path(project, merge_request) + + page.within(".new-note") do + click_button 'Invite Member' + end + + expect(page).to have_content("You're inviting members to the") + end +end diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb index 3099a893dc2..a6dfae72912 100644 --- a/spec/features/merge_request/user_posts_notes_spec.rb +++ b/spec/features/merge_request/user_posts_notes_spec.rb @@ -173,7 +173,7 @@ RSpec.describe 'Merge request > User posts notes', :js do it 'allows using markdown buttons after saving a note and then trying to edit it again' do page.within('.current-note-edit-form') do fill_in 'note[note]', with: 'This is the new content' - find('.btn-success').click + find('.btn-confirm').click end find('.note').hover @@ -182,16 +182,16 @@ RSpec.describe 'Merge request > User posts notes', :js do find('.js-note-edit').click page.within('.current-note-edit-form') do - expect(find('#note_note').value).to include('This is the new content') + expect(find_field('note[note]').value).to include('This is the new content') first('.js-md').click - expect(find('#note_note').value).to include('This is the new content****') + expect(find_field('note[note]').value).to include('This is the new content****') end end it 'appends the edited at time to the note' do page.within('.current-note-edit-form') do fill_in 'note[note]', with: 'Some new content' - find('.btn-success').click + find('.btn-confirm').click end page.within("#note_#{note.id}") do 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 caa04059469..9a3f97a0943 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 @@ -15,10 +15,6 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do diff_refs: merge_request.diff_refs) end - before do - stub_feature_flags(remove_resolve_note: false) - end - context 'no threads' do before do project.add_maintainer(user) @@ -67,7 +63,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do it 'allows user to mark thread as resolved' do page.within '.diff-content' do - click_button 'Resolve thread' + find('button[data-qa-selector="resolve_discussion_button"]').click end expect(page).to have_selector('.discussion-body', visible: false) @@ -84,7 +80,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do it 'allows user to unresolve thread' do page.within '.diff-content' do - click_button 'Resolve thread' + find('button[data-qa-selector="resolve_discussion_button"]').click click_button 'Unresolve thread' end @@ -96,7 +92,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do describe 'resolved thread' do before do page.within '.diff-content' do - click_button 'Resolve thread' + find('button[data-qa-selector="resolve_discussion_button"]').click end visit_merge_request @@ -197,7 +193,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do it 'allows user to resolve from reply form without a comment' do page.within '.diff-content' do - click_button 'Resolve thread' + find('button[data-qa-selector="resolve_discussion_button"]').click end page.within '.line-resolve-all-container' do @@ -234,7 +230,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do it 'hides jump to next button when all resolved' do page.within '.diff-content' do - click_button 'Resolve thread' + find('button[data-qa-selector="resolve_discussion_button"]').click end expect(page).to have_selector('.discussion-next-btn', visible: false) @@ -264,7 +260,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do visit_merge_request end - it 'does not mark thread as resolved when resolving single note' do + it 'marks thread as resolved when resolving single note' do page.within("#note_#{note.id}") do first('.line-resolve-btn').click @@ -273,15 +269,13 @@ 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 - expect(page).to have_content('Last updated') - page.within '.line-resolve-all-container' do - expect(page).to have_content('1 unresolved thread') + expect(page).to have_content('All threads resolved') end end it 'resolves thread' do - resolve_buttons = page.all('.note .line-resolve-btn', count: 2) + resolve_buttons = page.all('.note .line-resolve-btn', count: 1) resolve_buttons.each do |button| button.click end @@ -332,7 +326,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do it 'allows user to mark all threads as resolved' do page.all('.discussion-reply-holder', count: 2).each do |reply_holder| page.within reply_holder do - click_button 'Resolve thread' + find('button[data-qa-selector="resolve_discussion_button"]').click end end @@ -344,7 +338,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do it 'allows user to quickly scroll to next unresolved thread' do page.within('.discussion-reply-holder', match: :first) do - click_button 'Resolve thread' + find('button[data-qa-selector="resolve_discussion_button"]').click end page.within '.line-resolve-all-container' do @@ -416,7 +410,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do it 'allows user to mark thread as resolved' do page.within '.diff-content' do - click_button 'Resolve thread' + find('button[data-qa-selector="resolve_discussion_button"]').click end page.within '.diff-content .note' do @@ -431,7 +425,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do it 'allows user to unresolve thread' do page.within '.diff-content' do - click_button 'Resolve thread' + find('button[data-qa-selector="resolve_discussion_button"]').click click_button 'Unresolve thread' end @@ -459,7 +453,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do it 'allows user to comment & unresolve thread' do page.within '.diff-content' do - click_button 'Resolve thread' + find('button[data-qa-selector="resolve_discussion_button"]').click find_field('Reply…').click diff --git a/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb b/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb index 95e435a333e..d8b258bac47 100644 --- a/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb +++ b/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb @@ -13,7 +13,7 @@ RSpec.describe 'New merge request breadcrumb' do it 'displays link to project merge requests and new merge request' do page.within '.breadcrumbs' do - expect(find_link('Merge Requests')[:href]).to end_with(project_merge_requests_path(project)) + expect(find_link('Merge requests')[:href]).to end_with(project_merge_requests_path(project)) expect(find_link('New')[:href]).to end_with(project_new_merge_request_path(project)) end end diff --git a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb index ad0e9b48903..733b5a97fea 100644 --- a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb +++ b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb @@ -26,7 +26,6 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', end before do - stub_feature_flags(new_pipelines_table: false) stub_application_setting(auto_devops_enabled: false) stub_ci_pipeline_yaml_file(YAML.dump(config)) project.add_maintainer(user) @@ -62,7 +61,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', it 'sees branch pipelines and detached merge request pipelines in correct order' do page.within('.ci-table') do - expect(page).to have_selector('.ci-pending', count: 2) + expect(page).to have_selector('.ci-created', count: 2) expect(first('[data-testid="pipeline-url-link"]')).to have_content("##{detached_merge_request_pipeline.id}") end end @@ -154,7 +153,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', context 'when detached merge request pipeline succeeds' do before do - detached_merge_request_pipeline.succeed! + detached_merge_request_pipeline.reload.succeed! wait_for_requests end @@ -168,7 +167,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', context 'when branch pipeline succeeds' do before do click_link 'Overview' - push_pipeline.succeed! + push_pipeline.reload.succeed! wait_for_requests end @@ -197,7 +196,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', it 'sees a branch pipeline in pipeline tab' do page.within('.ci-table') do - expect(page).to have_selector('.ci-pending', count: 1) + expect(page).to have_selector('.ci-created', count: 1) expect(first('[data-testid="pipeline-url-link"]')).to have_content("##{push_pipeline.id}") end end @@ -333,6 +332,31 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', end end + context 'when the latest pipeline is running in the parent project' do + before do + Ci::CreatePipelineService.new(project, user, ref: 'feature') + .execute(:merge_request_event, merge_request: merge_request) + end + + context 'when the previous pipeline failed in the fork project' do + before do + detached_merge_request_pipeline.reload.drop! + end + + context 'when the parent project enables pipeline must succeed' do + before do + project.update!(only_allow_merge_if_pipeline_succeeds: true) + end + + it 'shows MWPS button' do + visit project_merge_request_path(project, merge_request) + + expect(page).to have_button('Merge when pipeline succeeds') + end + end + end + end + context 'when a user merges a merge request from a forked project to the parent project' do before do click_link("Overview") @@ -351,7 +375,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', context 'when detached merge request pipeline succeeds' do before do - detached_merge_request_pipeline.succeed! + detached_merge_request_pipeline.reload.succeed! wait_for_requests end @@ -364,7 +388,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', context 'when branch pipeline succeeds' do before do - push_pipeline.succeed! + push_pipeline.reload.succeed! wait_for_requests 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 05fa5459e06..0cb4107c21d 100644 --- a/spec/features/merge_request/user_sees_merge_widget_spec.rb +++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb @@ -32,7 +32,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do end it 'shows widget status after creating new merge request' do - click_button 'Submit merge request' + click_button 'Create merge request' wait_for_requests 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 c0dc2ec3baf..e5592ae9535 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 @@ -23,7 +23,7 @@ RSpec.describe 'Merge request < User sees mini pipeline graph', :js do end it 'displays a mini pipeline graph' do - expect(page).to have_selector('.mr-widget-pipeline-graph') + expect(page).to have_selector('[data-testid="pipeline-mini-graph"]') end context 'as json' do @@ -57,6 +57,10 @@ RSpec.describe 'Merge request < User sees mini pipeline graph', :js do first(dropdown_selector) end + before 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 diff --git a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb index ea46ae06329..b8b7fc2009f 100644 --- a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb +++ b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb @@ -28,8 +28,8 @@ RSpec.describe 'Merge request > User sees notes from forked project', :js do page.within('.discussion-notes') do find_field('Reply…').click - scroll_to(page.find('#note_note', visible: false)) - find('#note_note').send_keys('A reply comment') + scroll_to(find_field('note[note]', visible: false)) + fill_in 'note[note]', with: 'A reply comment' find('.js-comment-button').click end diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb index 77d2cb77ae3..a5047c8d550 100644 --- a/spec/features/merge_request/user_sees_pipelines_spec.rb +++ b/spec/features/merge_request/user_sees_pipelines_spec.rb @@ -41,7 +41,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js do context 'with a detached merge request pipeline' do let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline) } - it 'displays the Run Pipeline button' do + it 'displays the "Run pipeline" button' do visit project_merge_request_path(project, merge_request) page.within('.merge-request-tabs') do @@ -50,14 +50,14 @@ RSpec.describe 'Merge request > User sees pipelines', :js do wait_for_requests - expect(page.find('[data-testid="run_pipeline_button"]')).to have_text('Run Pipeline') + expect(page.find('[data-testid="run_pipeline_button"]')).to have_text('Run pipeline') end end context 'with a merged results pipeline' do let(:merge_request) { create(:merge_request, :with_merge_request_pipeline) } - it 'displays the Run Pipeline button' do + it 'displays the "Run pipeline" button' do visit project_merge_request_path(project, merge_request) page.within('.merge-request-tabs') do @@ -66,7 +66,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js do wait_for_requests - expect(page.find('[data-testid="run_pipeline_button"]')).to have_text('Run Pipeline') + expect(page.find('[data-testid="run_pipeline_button"]')).to have_text('Run pipeline') end end end @@ -131,7 +131,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js do visit project_merge_request_path(parent_project, merge_request) create_merge_request_pipeline - act_on_security_warning(action: 'Run Pipeline') + act_on_security_warning(action: 'Run pipeline') check_pipeline(expected_project: parent_project) check_head_pipeline(expected_project: parent_project) @@ -175,7 +175,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js do def create_merge_request_pipeline page.within('.merge-request-tabs') { click_link('Pipelines') } - click_button('Run Pipeline') + click_button('Run pipeline') end def check_pipeline(expected_project:) 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 9850ca3f173..275a87ca391 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 @@ -60,7 +60,7 @@ RSpec.describe 'Merge request > User selects branches for new MR', :js do expect(page).to have_content "wm.png" fill_in "merge_request_title", with: "Orphaned MR test" - click_button "Submit merge request" + click_button "Create merge request" click_button "Check out branch" @@ -200,7 +200,7 @@ RSpec.describe 'Merge request > User selects branches for new MR', :js do click_button "Compare branches" - expect(page).to have_button("Submit merge request") + expect(page).to have_button("Create merge request") end end end 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 84964bd0637..15f59c0d7bc 100644 --- a/spec/features/merge_request/user_squashes_merge_request_spec.rb +++ b/spec/features/merge_request/user_squashes_merge_request_spec.rb @@ -92,7 +92,7 @@ RSpec.describe 'User squashes a merge request', :js do before do visit project_new_merge_request_path(project, merge_request: { target_branch: 'master', source_branch: source_branch }) check 'merge_request[squash]' - click_on 'Submit merge request' + click_on 'Create merge request' wait_for_requests end @@ -121,7 +121,7 @@ RSpec.describe 'User squashes a merge request', :js do context 'when squash is not enabled on merge request creation', :sidekiq_might_not_need_inline do before do visit project_new_merge_request_path(project, merge_request: { target_branch: 'master', source_branch: source_branch }) - click_on 'Submit merge request' + click_on 'Create merge request' wait_for_requests end diff --git a/spec/features/merge_request/user_views_open_merge_request_spec.rb b/spec/features/merge_request/user_views_open_merge_request_spec.rb index 5f99d762ecb..f1b44010f63 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 @@ -125,7 +125,7 @@ RSpec.describe 'User views an open merge request' do end it 'encodes branch name' do - expect(find('cite.ref-name')[:title]).to eq(source_branch) + expect(find("[data-testid='ref-name']")[:title]).to eq(source_branch) end end end diff --git a/spec/features/milestones/user_creates_milestone_spec.rb b/spec/features/milestones/user_creates_milestone_spec.rb index d80796b8f9a..dd377aa4a26 100644 --- a/spec/features/milestones/user_creates_milestone_spec.rb +++ b/spec/features/milestones/user_creates_milestone_spec.rb @@ -14,7 +14,7 @@ RSpec.describe "User creates milestone", :js do end it "creates milestone" do - title = "v2.3".freeze + title = "v2.3" fill_in("Title", with: title) fill_in("Description", with: "# Description header") diff --git a/spec/features/milestones/user_views_milestone_spec.rb b/spec/features/milestones/user_views_milestone_spec.rb index 9c19f842427..8674d59afdf 100644 --- a/spec/features/milestones/user_views_milestone_spec.rb +++ b/spec/features/milestones/user_views_milestone_spec.rb @@ -98,7 +98,7 @@ RSpec.describe "User views milestone" do visit(project_milestone_path(project, milestone)) within('.js-milestone-tabs') do - click_link('Merge Requests') + click_link('Merge requests') end wait_for_requests @@ -116,7 +116,7 @@ RSpec.describe "User views milestone" do visit(group_milestone_path(group, group_milestone)) within('.js-milestone-tabs') do - click_link('Merge Requests') + click_link('Merge requests') end expect(page.find('#tab-merge-requests')).to have_text(project.name) diff --git a/spec/features/milestones/user_views_milestones_spec.rb b/spec/features/milestones/user_views_milestones_spec.rb index f8b4b802a60..58439df92ba 100644 --- a/spec/features/milestones/user_views_milestones_spec.rb +++ b/spec/features/milestones/user_views_milestones_spec.rb @@ -18,7 +18,7 @@ RSpec.describe "User views milestones" do expect(page).to have_content(milestone.title) .and have_content(milestone.expires_at) .and have_content("Issues") - .and have_content("Merge Requests") + .and have_content("Merge requests") end context "with issues", :js do @@ -80,7 +80,6 @@ RSpec.describe "User views milestones with no MR" do expect(page).to have_content(milestone.title) .and have_content(milestone.expires_at) .and have_content("Issues") - .and have_no_content("Merge Requests") end it "opens milestone" do diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb index b22778012a8..2781cfffbaf 100644 --- a/spec/features/participants_autocomplete_spec.rb +++ b/spec/features/participants_autocomplete_spec.rb @@ -3,9 +3,9 @@ require 'spec_helper' RSpec.describe 'Member autocomplete', :js do - let(:project) { create(:project, :public) } - let(:user) { create(:user) } - let(:author) { create(:user) } + let_it_be(:project) { create(:project, :public, :repository) } + let_it_be(:user) { create(:user) } + let_it_be(:author) { create(:user) } let(:note) { create(:note, noteable: noteable, project: noteable.project) } before do @@ -15,20 +15,16 @@ RSpec.describe 'Member autocomplete', :js do shared_examples "open suggestions when typing @" do |resource_name| before do - page.within('.new-note') do - if resource_name == 'commit' - find('#note_note').send_keys('@') - else - find('#note-body').send_keys('@') - end + if resource_name == 'commit' + fill_in 'note[note]', with: '@' + else + fill_in 'Comment', with: '@' end end it 'suggests noteable author and note author' do - page.within('.atwho-view', visible: true) do - expect(page).to have_content(author.username) - expect(page).to have_content(note.author.username) - end + expect(find_autocomplete_menu).to have_text(author.username) + expect(find_autocomplete_menu).to have_text(note.author.username) end end @@ -51,22 +47,17 @@ RSpec.describe 'Member autocomplete', :js do stub_feature_flags(tribute_autocomplete: true) visit project_issue_path(project, noteable) - page.within('.new-note') do - find('#note-body').send_keys('@') - end + fill_in 'Comment', with: '@' end it 'suggests noteable author and note author' do - page.within('.tribute-container', visible: true) do - expect(page).to have_content(author.username) - expect(page).to have_content(note.author.username) - end + expect(find_tribute_autocomplete_menu).to have_content(author.username) + expect(find_tribute_autocomplete_menu).to have_content(note.author.username) end end end context 'adding a new note on a Merge Request' do - let(:project) { create(:project, :public, :repository) } let(:noteable) do create(:merge_request, source_project: project, target_project: project, author: author) @@ -80,7 +71,6 @@ RSpec.describe 'Member autocomplete', :js do end context 'adding a new note on a Commit' do - let(:project) { create(:project, :public, :repository) } let(:noteable) { project.commit } let(:note) { create(:note_on_commit, project: project, commit_id: project.commit.id) } @@ -94,4 +84,14 @@ RSpec.describe 'Member autocomplete', :js do include_examples "open suggestions when typing @", 'commit' end + + private + + def find_autocomplete_menu + find('.atwho-view ul', visible: true) + end + + def find_tribute_autocomplete_menu + find('.tribute-container ul', visible: true) + end end diff --git a/spec/features/profiles/emails_spec.rb b/spec/features/profiles/emails_spec.rb index bdf1f8b022a..6b6f628e2d5 100644 --- a/spec/features/profiles/emails_spec.rb +++ b/spec/features/profiles/emails_spec.rb @@ -43,7 +43,7 @@ RSpec.describe 'Profile > Emails' do end it 'user removes email' do - user.emails.create(email: 'my@email.com') + user.emails.create!(email: 'my@email.com') visit profile_emails_path expect(page).to have_content("my@email.com") @@ -52,7 +52,7 @@ RSpec.describe 'Profile > Emails' do end it 'user confirms email' do - email = user.emails.create(email: 'my@email.com') + email = user.emails.create!(email: 'my@email.com') visit profile_emails_path expect(page).to have_content("#{email.email} Unverified") @@ -64,7 +64,7 @@ RSpec.describe 'Profile > Emails' do end it 'user re-sends confirmation email' do - email = user.emails.create(email: 'my@email.com') + email = user.emails.create!(email: 'my@email.com') visit profile_emails_path expect { click_link("Resend confirmation email") }.to have_enqueued_job.on_queue('mailers') @@ -72,7 +72,7 @@ RSpec.describe 'Profile > Emails' do end it 'old unconfirmed emails show Send Confirmation button' do - email = user.emails.create(email: 'my@email.com') + email = user.emails.create!(email: 'my@email.com') email.update_attribute(:confirmation_sent_at, nil) visit profile_emails_path diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb index 039966080d8..c9059395377 100644 --- a/spec/features/profiles/password_spec.rb +++ b/spec/features/profiles/password_spec.rb @@ -119,7 +119,7 @@ RSpec.describe 'Profile > Password' do before do sign_in(user) - user.update(password_expires_at: 1.hour.ago) + user.update!(password_expires_at: 1.hour.ago) user.identities.delete expect(user.ldap_user?).to eq false end diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb index 9e56ef087ae..c85657c89d5 100644 --- a/spec/features/profiles/personal_access_tokens_spec.rb +++ b/spec/features/profiles/personal_access_tokens_spec.rb @@ -95,7 +95,7 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do end it "removes expired tokens from 'active' section" do - personal_access_token.update(expires_at: 5.days.ago) + personal_access_token.update!(expires_at: 5.days.ago) visit profile_personal_access_tokens_path expect(page).to have_selector(".settings-message") diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb index bd4917824d1..57f7c7878e3 100644 --- a/spec/features/profiles/user_edit_profile_spec.rb +++ b/spec/features/profiles/user_edit_profile_spec.rb @@ -212,8 +212,10 @@ RSpec.describe 'User edit profile' do end it 'shows author as busy in the assignee dropdown' do - find('.block.assignee .edit-link').click - wait_for_requests + page.within('.assignee') do + click_button('Edit') + wait_for_requests + end page.within '.dropdown-menu-user' do expect(page).to have_content("#{user.name} (Busy)") @@ -227,7 +229,7 @@ RSpec.describe 'User edit profile' do visit project_issue_path(project, issue) wait_for_requests - expect(page.find('[data-testid="expanded-assignee"]')).to have_text("#{user.name} (Busy)") + expect(page.find('.issuable-assignees')).to have_content("#{user.name} (Busy)") end end diff --git a/spec/features/profiles/user_search_settings_spec.rb b/spec/features/profiles/user_search_settings_spec.rb index 60df0d7532b..64a8556e349 100644 --- a/spec/features/profiles/user_search_settings_spec.rb +++ b/spec/features/profiles/user_search_settings_spec.rb @@ -10,9 +10,11 @@ RSpec.describe 'User searches their settings', :js do end context 'in profile page' do - let(:visit_path) { profile_path } + before do + visit profile_path + end - it_behaves_like 'can search settings with feature flag check', 'Public Avatar', 'Main settings' + it_behaves_like 'can search settings', 'Public Avatar', 'Main settings' end context 'in preferences page' do diff --git a/spec/features/projects/active_tabs_spec.rb b/spec/features/projects/active_tabs_spec.rb index 86fe59f003f..9de43e7d18c 100644 --- a/spec/features/projects/active_tabs_spec.rb +++ b/spec/features/projects/active_tabs_spec.rb @@ -79,7 +79,7 @@ RSpec.describe 'Project active tab' do visit project_merge_requests_path(project) end - it_behaves_like 'page has active tab', 'Merge Requests' + it_behaves_like 'page has active tab', 'Merge requests' end context 'on project Wiki' do diff --git a/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb b/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb index a65a82fab43..1a368676a5e 100644 --- a/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb +++ b/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb @@ -44,17 +44,6 @@ RSpec.describe 'Blob button line permalinks (BlobLinePermalinkUpdater)', :js do expect(find('.js-data-file-blob-permalink-url')['href']).to eq(get_absolute_url(project_blob_path(project, tree_join(sha, path), anchor: ending_fragment))) end - it 'changes fragment hash if icon inside line number link is clicked' do - ending_fragment = "L7" - - visit_blob - - find("##{ending_fragment}").hover - find("##{ending_fragment} svg").click - - expect(find('.js-data-file-blob-permalink-url')['href']).to eq(get_absolute_url(project_blob_path(project, tree_join(sha, path), anchor: ending_fragment))) - end - it 'with initial fragment hash, changes fragment hash if line number clicked' do fragment = "L1" ending_fragment = "L5" @@ -94,17 +83,6 @@ RSpec.describe 'Blob button line permalinks (BlobLinePermalinkUpdater)', :js do expect(find('.js-blob-blame-link')['href']).to eq(get_absolute_url(project_blame_path(project, tree_join('master', path), anchor: ending_fragment))) end - it 'changes fragment hash if icon inside line number link is clicked' do - ending_fragment = "L7" - - visit_blob - - find("##{ending_fragment}").hover - find("##{ending_fragment} svg").click - - expect(find('.js-blob-blame-link')['href']).to eq(get_absolute_url(project_blame_path(project, tree_join('master', path), anchor: ending_fragment))) - end - it 'with initial fragment hash, changes fragment hash if line number clicked' do fragment = "L1" ending_fragment = "L5" diff --git a/spec/features/projects/blobs/user_views_pipeline_editor_button_spec.rb b/spec/features/projects/blobs/user_views_pipeline_editor_button_spec.rb new file mode 100644 index 00000000000..b872fa701c8 --- /dev/null +++ b/spec/features/projects/blobs/user_views_pipeline_editor_button_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'User views pipeline editor button on root ci config file', :js do + include BlobSpecHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :public, :repository) } + + context "when the ci config is the root file" do + before do + project.add_developer(user) + sign_in(user) + end + + it 'shows the button to the Pipeline Editor' do + project.update!(ci_config_path: '.my-config.yml') + project.repository.create_file(user, project.ci_config_path_or_default, 'test', message: 'testing', branch_name: 'master') + visit project_blob_path(project, File.join('master', '.my-config.yml')) + + expect(page).to have_content('Pipeline Editor') + end + + it 'does not shows the Pipeline Editor button' do + project.repository.create_file(user, '.my-sub-config.yml', 'test', message: 'testing', branch_name: 'master') + visit project_blob_path(project, File.join('master', '.my-sub-config.yml')) + + expect(page).not_to have_content('Pipeline Editor') + end + end + + context "when user cannot collaborate" do + before do + sign_in(user) + end + it 'does not shows the Pipeline Editor button' do + visit project_blob_path(project, File.join('master', '.my-config.yml')) + expect(page).not_to have_content('Pipeline Editor') + end + end +end diff --git a/spec/features/projects/branches/user_creates_branch_spec.rb b/spec/features/projects/branches/user_creates_branch_spec.rb index 52c860bfe36..18d083f7d88 100644 --- a/spec/features/projects/branches/user_creates_branch_spec.rb +++ b/spec/features/projects/branches/user_creates_branch_spec.rb @@ -16,7 +16,7 @@ RSpec.describe "User creates branch", :js do end it "creates new branch" do - branch_name = "deploy_keys".freeze + branch_name = "deploy_keys" create_branch(branch_name) @@ -25,7 +25,7 @@ RSpec.describe "User creates branch", :js do context "when branch name is invalid" do it "does not create new branch" do - invalid_branch_name = "1.0 stable".freeze + invalid_branch_name = "1.0 stable" fill_in("branch_name", with: invalid_branch_name) page.find("body").click # defocus the branch_name input diff --git a/spec/features/projects/branches/user_deletes_branch_spec.rb b/spec/features/projects/branches/user_deletes_branch_spec.rb index c480c41709c..bebb4bb679b 100644 --- a/spec/features/projects/branches/user_deletes_branch_spec.rb +++ b/spec/features/projects/branches/user_deletes_branch_spec.rb @@ -9,12 +9,15 @@ RSpec.describe "User deletes branch", :js do before do project.add_developer(user) sign_in(user) - - visit(project_branches_path(project)) end it "deletes branch" do - fill_in("branch-search", with: "improve/awesome").native.send_keys(:enter) + visit(project_branches_path(project)) + + branch_search = find('input[data-testid="branch-search"]') + + branch_search.set('improve/awesome') + branch_search.native.send_keys(:enter) page.within(".js-branch-improve\\/awesome") do accept_alert { find(".btn-danger").click } diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb index 4bfe8852291..f805416b03d 100644 --- a/spec/features/projects/branches_spec.rb +++ b/spec/features/projects/branches_spec.rb @@ -88,8 +88,10 @@ RSpec.describe 'Branches' do it 'shows filtered branches', :js do visit project_branches_path(project) - fill_in 'branch-search', with: 'fix' - find('#branch-search').native.send_keys(:enter) + branch_search = find('input[data-testid="branch-search"]') + + branch_search.set('fix') + branch_search.native.send_keys(:enter) expect(page).to have_content('fix') expect(find('.all-branches')).to have_selector('li', count: 1) @@ -114,20 +116,24 @@ RSpec.describe 'Branches' do expect(page).to have_content(sorted_branches(repository, count: 20, sort_by: :updated_desc)) end - it 'sorts the branches by name' do + it 'sorts the branches by name', :js do visit project_branches_filtered_path(project, state: 'all') click_button "Last updated" # Open sorting dropdown - click_link "Name" + within '[data-testid="branches-dropdown"]' do + find('p', text: 'Name').click + end expect(page).to have_content(sorted_branches(repository, count: 20, sort_by: :name)) end - it 'sorts the branches by oldest updated' do + it 'sorts the branches by oldest updated', :js do visit project_branches_filtered_path(project, state: 'all') click_button "Last updated" # Open sorting dropdown - click_link "Oldest updated" + within '[data-testid="branches-dropdown"]' do + find('p', text: 'Oldest updated').click + end expect(page).to have_content(sorted_branches(repository, count: 20, sort_by: :updated_asc)) end @@ -145,8 +151,10 @@ RSpec.describe 'Branches' do it 'shows filtered branches', :js do visit project_branches_filtered_path(project, state: 'all') - fill_in 'branch-search', with: 'fix' - find('#branch-search').native.send_keys(:enter) + branch_search = find('input[data-testid="branch-search"]') + + branch_search.set('fix') + branch_search.native.send_keys(:enter) expect(page).to have_content('fix') expect(find('.all-branches')).to have_selector('li', count: 1) @@ -157,9 +165,10 @@ RSpec.describe 'Branches' do it 'removes branch after confirmation', :js do visit project_branches_filtered_path(project, state: 'all') - fill_in 'branch-search', with: 'fix' + branch_search = find('input[data-testid="branch-search"]') - find('#branch-search').native.send_keys(:enter) + branch_search.set('fix') + branch_search.native.send_keys(:enter) expect(page).to have_content('fix') expect(find('.all-branches')).to have_selector('li', count: 1) diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb index d34dde6a8f2..8c497cded8e 100644 --- a/spec/features/projects/clusters/gcp_spec.rb +++ b/spec/features/projects/clusters/gcp_spec.rb @@ -119,7 +119,7 @@ RSpec.describe 'Gcp Cluster', :js do context 'when user disables the cluster' do before do page.find(:css, '.js-cluster-enable-toggle-area .js-project-feature-toggle').click - page.within('.js-cluster-integration-form') { click_button 'Save changes' } + page.within('.js-cluster-details-form') { click_button 'Save changes' } end it 'user sees the successful message' do diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb index 748eba558aa..5b60edbcf87 100644 --- a/spec/features/projects/clusters/user_spec.rb +++ b/spec/features/projects/clusters/user_spec.rb @@ -84,7 +84,7 @@ RSpec.describe 'User Cluster', :js do context 'when user disables the cluster' do before do page.find(:css, '.js-cluster-enable-toggle-area .js-project-feature-toggle').click - page.within('.js-cluster-integration-form') { click_button 'Save changes' } + page.within('.js-cluster-details-form') { click_button 'Save changes' } end it 'user sees the successful message' do diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb index 6da66989b09..6b03301aa74 100644 --- a/spec/features/projects/clusters_spec.rb +++ b/spec/features/projects/clusters_spec.rb @@ -58,7 +58,7 @@ RSpec.describe 'Clusters', :js do before do click_link 'default-cluster' fill_in 'cluster_environment_scope', with: 'production/*' - within '.js-cluster-integration-form' do + within '.js-cluster-details-form' do click_button 'Save changes' end end @@ -149,7 +149,7 @@ RSpec.describe 'Clusters', :js do before do click_link 'default-cluster' fill_in 'cluster_environment_scope', with: 'production/*' - within ".js-cluster-integration-form" do + within ".js-cluster-details-form" do click_button 'Save changes' end end diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb index 489a90cc8fc..cd944436228 100644 --- a/spec/features/projects/commit/cherry_pick_spec.rb +++ b/spec/features/projects/commit/cherry_pick_spec.rb @@ -91,7 +91,7 @@ RSpec.describe 'Cherry-pick Commits', :js do context 'when the project is archived' do let(:project) { create(:project, :repository, :archived, namespace: user.namespace) } - it 'does not show the cherry-pick link' do + it 'does not show the cherry-pick button' do open_dropdown expect(page).not_to have_text("Cherry-pick") @@ -106,12 +106,15 @@ RSpec.describe 'Cherry-pick Commits', :js do end def open_dropdown - find('.header-action-buttons .dropdown').click + find(dropdown_selector).click end def open_modal open_dropdown - find('[data-testid="cherry-pick-commit-link"]').click + + page.within(dropdown_selector) do + click_button 'Cherry-pick' + end end def submit_cherry_pick(create_merge_request: false) @@ -121,6 +124,10 @@ RSpec.describe 'Cherry-pick Commits', :js do end end + def dropdown_selector + '[data-testid="commit-options-dropdown"]' + end + def modal_selector '[data-testid="modal-commit"]' end diff --git a/spec/features/projects/commit/comments/user_edits_comments_spec.rb b/spec/features/projects/commit/comments/user_edits_comments_spec.rb index 787d8cdb02b..8ac15c9cb7f 100644 --- a/spec/features/projects/commit/comments/user_edits_comments_spec.rb +++ b/spec/features/projects/commit/comments/user_edits_comments_spec.rb @@ -19,7 +19,7 @@ RSpec.describe "User edits a comment on a commit", :js do end it "edits comment" do - new_comment_text = "+1 Awesome!".freeze + new_comment_text = "+1 Awesome!" page.within(".main-notes-list") do note = find(".note") diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb index 7d206f76031..6de02556175 100644 --- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb +++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb @@ -16,46 +16,28 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do let(:build) { create(:ci_build, pipeline: pipeline, status: :running) } - shared_examples 'shows ci icon and mini pipeline' do - before do - build.run - visit project_commit_path(project, project.commit.id) - end - - it 'display icon with status' do - expect(page).to have_selector('.ci-status-icon-running') - end - - it 'displays a mini pipeline graph' do - expect(page).to have_selector('.mr-widget-pipeline-graph') - - first('.mini-pipeline-graph-dropdown-toggle').click - - wait_for_requests - - page.within '.js-builds-dropdown-list' do - expect(page).to have_selector('.ci-status-icon-running') - expect(page).to have_content(build.stage) - end + before do + build.run + visit project_commit_path(project, project.commit.id) + end - build.drop - end + it 'display icon with status' do + expect(page).to have_selector('.ci-status-icon-running') end - context 'when ci_commit_pipeline_mini_graph_vue is disabled' do - before do - stub_feature_flags(ci_commit_pipeline_mini_graph_vue: false) - end + it 'displays a mini pipeline graph' do + expect(page).to have_selector('[data-testid="pipeline-mini-graph"]') - it_behaves_like 'shows ci icon and mini pipeline' - end + first('.mini-pipeline-graph-dropdown-toggle').click - context 'when ci_commit_pipeline_mini_graph_vue is enabled' do - before do - stub_feature_flags(ci_commit_pipeline_mini_graph_vue: true) + wait_for_requests + + page.within '.js-builds-dropdown-list' do + expect(page).to have_selector('.ci-status-icon-running') + expect(page).to have_content(build.stage) end - it_behaves_like 'shows ci icon and mini pipeline' + build.drop end end @@ -65,7 +47,7 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do end it 'does not display a mini pipeline graph' do - expect(page).not_to have_selector('.mr-widget-pipeline-graph') + expect(page).not_to have_selector('[data-testid="pipeline-mini-graph"]') end end end diff --git a/spec/features/projects/commit/user_reverts_commit_spec.rb b/spec/features/projects/commit/user_reverts_commit_spec.rb index 72c639a027e..ad327b86aa7 100644 --- a/spec/features/projects/commit/user_reverts_commit_spec.rb +++ b/spec/features/projects/commit/user_reverts_commit_spec.rb @@ -62,10 +62,10 @@ RSpec.describe 'User reverts a commit', :js do context 'when the project is archived' do let(:project) { create(:project, :repository, :archived, namespace: user.namespace) } - it 'does not show the revert link' do + it 'does not show the revert button' do open_dropdown - expect(page).not_to have_link('Revert') + expect(page).not_to have_button('Revert') end end end @@ -75,17 +75,24 @@ RSpec.describe 'User reverts a commit', :js do page.within(modal_selector) do uncheck('create_merge_request') unless create_merge_request - click_button('Revert') + click_button 'Revert' end end def open_dropdown - find('.header-action-buttons .dropdown').click + find(dropdown_selector).click end def open_modal open_dropdown - find('[data-testid="revert-commit-link"]').click + + page.within(dropdown_selector) do + click_button 'Revert' + end + end + + def dropdown_selector + '[data-testid="commit-options-dropdown"]' end def modal_selector diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb index 4894e2b7f3e..76162fb800a 100644 --- a/spec/features/projects/commits/user_browses_commits_spec.rb +++ b/spec/features/projects/commits/user_browses_commits_spec.rb @@ -20,9 +20,14 @@ RSpec.describe 'User browses commits' do .and have_content('Side-by-side') end - it 'fill commit sha when click new tag from commit page' do + it 'fill commit sha when click new tag from commit page', :js do + dropdown_selector = '[data-testid="commit-options-dropdown"]' visit project_commit_path(project, sample_commit.id) - click_link 'Tag' + find(dropdown_selector).click + + page.within(dropdown_selector) do + click_link 'Tag' + end expect(page).to have_selector("input[value='#{sample_commit.id}']", visible: false) end diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb index c94247f65d2..ab82a4750d3 100644 --- a/spec/features/projects/features_visibility_spec.rb +++ b/spec/features/projects/features_visibility_spec.rb @@ -134,7 +134,7 @@ RSpec.describe 'Edit Project Settings' do it 'renders 200 if user is member of group' do group = create(:group) project.group = group - project.save + project.save! group.add_owner(member) diff --git a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb index 55b9f38d8e7..b0ccb5fca94 100644 --- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb +++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb @@ -5,10 +5,14 @@ require 'spec_helper' RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file', :js do include Spec::Support::Helpers::Features::EditorLiteSpecHelpers + let(:params) { {} } + let(:filename) { '.gitlab-ci.yml' } + + let_it_be(:project) { create(:project, :repository) } + before do - project = create(:project, :repository) sign_in project.owner - visit project_new_blob_path(project, 'master', file_name: '.gitlab-ci.yml') + visit project_new_blob_path(project, 'master', file_name: filename, **params) end it 'user can pick a template from the dropdown' do @@ -29,4 +33,38 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file', :js expect(editor_get_value).to have_content('This file is a template, and might need editing before it works on your project') expect(editor_get_value).to have_content('jekyll build -d test') end + + context 'when template param is provided' do + let(:params) { { template: 'Jekyll' } } + + it 'uses the given template' do + wait_for_requests + + expect(page).to have_css('.gitlab-ci-yml-selector .dropdown-toggle-text', text: 'Apply a template') + expect(editor_get_value).to have_content('This file is a template, and might need editing before it works on your project') + expect(editor_get_value).to have_content('jekyll build -d test') + end + end + + context 'when provided template param is not a valid template name' do + let(:params) { { template: 'non-existing-template' } } + + it 'leaves the editor empty' do + wait_for_requests + + expect(page).to have_css('.gitlab-ci-yml-selector .dropdown-toggle-text', text: 'Apply a template') + expect(editor_get_value).to have_content('') + end + end + + context 'when template is not available for the given file' do + let(:filename) { 'Dockerfile' } + let(:params) { { template: 'Jekyll' } } + + it 'leaves the editor empty' do + wait_for_requests + + expect(editor_get_value).to have_content('') + end + end end diff --git a/spec/features/projects/files/user_creates_directory_spec.rb b/spec/features/projects/files/user_creates_directory_spec.rb index f2074c78dba..46b93d738e1 100644 --- a/spec/features/projects/files/user_creates_directory_spec.rb +++ b/spec/features/projects/files/user_creates_directory_spec.rb @@ -77,7 +77,7 @@ RSpec.describe 'Projects > Files > User creates a directory', :js do it 'creates the directory in the new branch and redirect to the merge request' do expect(page).to have_content('new-feature') expect(page).to have_content('The directory has been successfully created') - expect(page).to have_content('New Merge Request') + expect(page).to have_content('New merge request') expect(page).to have_content('From new-feature into master') expect(page).to have_content('Add new directory') diff --git a/spec/features/projects/files/user_uploads_files_spec.rb b/spec/features/projects/files/user_uploads_files_spec.rb index 944d08df3f3..54e816d3d13 100644 --- a/spec/features/projects/files/user_uploads_files_spec.rb +++ b/spec/features/projects/files/user_uploads_files_spec.rb @@ -3,8 +3,6 @@ require 'spec_helper' RSpec.describe 'Projects > Files > User uploads files' do - include DropzoneHelper - let(:user) { create(:user) } let(:project) { create(:project, :repository, name: 'Shop', creator: user) } let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } @@ -17,36 +15,17 @@ RSpec.describe 'Projects > Files > User uploads files' do context 'when a user has write access' do before do visit(project_tree_path(project)) - end - - include_examples 'it uploads and commit a new text file' - - include_examples 'it uploads and commit a new image file' - it 'uploads a file to a sub-directory', :js do - click_link 'files' - - page.within('.repo-breadcrumb') do - expect(page).to have_content('files') - end - - find('.add-to-tree').click - click_link('Upload file') - drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt')) + wait_for_requests + end - page.within('#modal-upload-blob') do - fill_in(:commit_message, with: 'New commit message') - end + include_examples 'it uploads and commits a new text file' - click_button('Upload file') + include_examples 'it uploads and commits a new image file' - expect(page).to have_content('New commit message') + include_examples 'it uploads and commits a new pdf file' - page.within('.repo-breadcrumb') do - expect(page).to have_content('files') - expect(page).to have_content('doc_sample.txt') - end - end + include_examples 'it uploads a file to a sub-directory' end context 'when a user does not have write access' do @@ -56,6 +35,6 @@ RSpec.describe 'Projects > Files > User uploads files' do visit(project_tree_path(project2)) end - include_examples 'it uploads and commit a new file to a forked project' + include_examples 'it uploads and commits a new file to a forked project' end end diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb index 7abbd207b24..2b7ea70fe5a 100644 --- a/spec/features/projects/fork_spec.rb +++ b/spec/features/projects/fork_spec.rb @@ -76,7 +76,7 @@ RSpec.describe 'Project fork' do let(:forking_access_level) { ProjectFeature::PRIVATE } before do - project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL) + project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL) end context 'user is not a team member' do @@ -118,6 +118,50 @@ RSpec.describe 'Project fork' do it_behaves_like 'fork button on project page' it_behaves_like 'create fork page', 'Fork project' + context 'fork form', :js do + let(:group) { create(:group) } + let(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user } + + def submit_form + select(group.name) + click_button 'Fork project' + end + + it 'forks the project', :sidekiq_might_not_need_inline do + visit new_project_fork_path(project) + submit_form + + expect(page).to have_content 'Forked from' + end + + it 'shows the new forked project on the forks page' do + visit new_project_fork_path(project) + submit_form + wait_for_requests + + visit project_forks_path(project) + + page.within('.js-projects-list-holder') do + expect(page).to have_content("#{group.name} / #{project.name}") + end + end + + it 'shows the filled in info forked project on the forks page' do + fork_name = 'some-name' + visit new_project_fork_path(project) + fill_in('fork-name', with: fork_name, fill_options: { clear: :backspace }) + fill_in('fork-slug', with: fork_name, fill_options: { clear: :backspace }) + submit_form + wait_for_requests + + visit project_forks_path(project) + + page.within('.js-projects-list-holder') do + expect(page).to have_content("#{group.name} / #{fork_name}") + end + end + end + context 'with fork_project_form feature flag disabled' do before do stub_feature_flags(fork_project_form: false) @@ -164,7 +208,7 @@ RSpec.describe 'Project fork' do expect(page).to have_content(/new merge request/i) page.within '.nav-sidebar' do - first(:link, 'Merge Requests').click + first(:link, 'Merge requests').click end expect(page).to have_content(/new merge request/i) diff --git a/spec/features/projects/jobs/permissions_spec.rb b/spec/features/projects/jobs/permissions_spec.rb index e87880d74b1..140d5dee270 100644 --- a/spec/features/projects/jobs/permissions_spec.rb +++ b/spec/features/projects/jobs/permissions_spec.rb @@ -12,6 +12,8 @@ RSpec.describe 'Project Jobs Permissions' do let_it_be(:job) { create(:ci_build, :running, :coverage, :trace_artifact, pipeline: pipeline) } before do + stub_feature_flags(jobs_table_vue: false) + sign_in(user) project.enable_ci diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb index 5abebf2320e..dbcd7b5caf5 100644 --- a/spec/features/projects/jobs/user_browses_jobs_spec.rb +++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb @@ -9,6 +9,7 @@ RSpec.describe 'User browses jobs' do let(:user) { create(:user) } before do + stub_feature_flags(jobs_table_vue: false) project.add_maintainer(user) project.enable_ci project.update_attribute(:build_coverage_regex, /Coverage (\d+)%/) @@ -24,14 +25,6 @@ RSpec.describe 'User browses jobs' do end end - it 'shows the "CI Lint" button' do - page.within('.nav-controls') do - ci_lint_tool_link = page.find_link('CI Lint') - - expect(ci_lint_tool_link[:href]).to end_with(project_ci_lint_path(project)) - end - end - context 'with a failed job' do let!(:build) { create(:ci_build, :coverage, :failed, pipeline: pipeline) } diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index 7811394b541..18a6ad12240 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -20,6 +20,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do end before do + stub_feature_flags(jobs_table_vue: false) project.add_role(user, user_access_level) sign_in(user) end @@ -32,7 +33,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do it 'shows the empty state page' do expect(page).to have_content('Use jobs to automate your tasks') - expect(page).to have_link('Create CI/CD configuration file', href: project.present(current_user: user).add_ci_yml_path) + expect(page).to have_link('Create CI/CD configuration file', href: project_ci_pipeline_editor_path(project)) end end @@ -1057,7 +1058,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do before do job.run! job.cancel! - project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) sign_out(:user) sign_in(create(:user)) diff --git a/spec/features/projects/labels/user_removes_labels_spec.rb b/spec/features/projects/labels/user_removes_labels_spec.rb index 217f86b92cf..11d73a56965 100644 --- a/spec/features/projects/labels/user_removes_labels_spec.rb +++ b/spec/features/projects/labels/user_removes_labels_spec.rb @@ -18,17 +18,17 @@ RSpec.describe "User removes labels" do visit(project_labels_path(project)) end - it "removes label" do + it "removes label", :js do page.within(".other-labels") do page.first(".label-list-item") do first('.js-label-options-dropdown').click - first(".remove-row").click + first('.js-delete-label-modal-button').click end + end - expect(page).to have_content("#{label.title} will be permanently deleted from #{project.name}. This cannot be undone.") + expect(page).to have_content("#{label.title} will be permanently deleted from #{project.name}. This cannot be undone.") - first(:link, "Delete label").click - end + first(:link, "Delete label").click expect(page).to have_content("Label was removed").and have_no_content(label.title) end diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb index 0830585da9b..384b8ae9929 100644 --- a/spec/features/projects/members/list_spec.rb +++ b/spec/features/projects/members/list_spec.rb @@ -175,7 +175,7 @@ RSpec.describe 'Project members list', :js do click_on 'Invite members' page.within '#invite-members-modal' do - fill_in 'Search for members to invite', with: id + fill_in 'Select members or type email addresses', with: id wait_for_requests click_button id diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb index a339130ee3c..7073741a92d 100644 --- a/spec/features/projects/members/user_requests_access_spec.rb +++ b/spec/features/projects/members/user_requests_access_spec.rb @@ -13,7 +13,7 @@ RSpec.describe 'Projects > Members > User requests access', :js do end it 'request access feature is disabled' do - project.update(request_access_enabled: false) + project.update!(request_access_enabled: false) visit project_path(project) expect(page).not_to have_content 'Request Access' diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb index 9547ba8a390..93bbabcc3f8 100644 --- a/spec/features/projects/merge_request_button_spec.rb +++ b/spec/features/projects/merge_request_button_spec.rb @@ -14,7 +14,9 @@ RSpec.describe 'Merge Request button' do it 'does not show Create merge request button' do visit url - expect(page).not_to have_link(label) + within '.content-wrapper' do + expect(page).not_to have_link(label) + end end end diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb index 4ff3827b240..7dc3ee63669 100644 --- a/spec/features/projects/navbar_spec.rb +++ b/spec/features/projects/navbar_spec.rb @@ -13,6 +13,8 @@ RSpec.describe 'Project navbar' do before do insert_package_nav(_('Operations')) + insert_infrastructure_registry_nav + stub_config(registry: { enabled: false }) project.add_maintainer(user) sign_in(user) @@ -60,7 +62,7 @@ RSpec.describe 'Project navbar' do before do stub_config(registry: { enabled: true }) - insert_container_nav(_('Operations')) + insert_container_nav visit project_path(project) end diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index ec34640bd00..7119039d5ff 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -12,6 +12,72 @@ RSpec.describe 'New project', :js do sign_in(user) end + context 'new repo experiment', :experiment do + it 'when in control renders "project"' do + stub_experiments(new_repo: :control) + + visit new_project_path + + find('li.header-new.dropdown').click + + page.within('li.header-new.dropdown') do + expect(page).to have_selector('a', text: 'New project') + expect(page).to have_no_selector('a', text: 'New project/repository') + end + + expect(page).to have_selector('.blank-state-title', text: 'Create blank project') + expect(page).to have_no_selector('.blank-state-title', text: 'Create blank project/repository') + end + + it 'when in candidate renders "project/repository"' do + stub_experiments(new_repo: :candidate) + + visit new_project_path + + find('li.header-new.dropdown').click + + page.within('li.header-new.dropdown') do + expect(page).to have_selector('a', text: 'New project/repository') + end + + expect(page).to have_selector('.blank-state-title', text: 'Create blank project/repository') + end + + context 'with combined_menu feature disabled' do + before do + stub_feature_flags(combined_menu: false) + end + + it 'when in control it renders "project" in the new projects dropdown' do + stub_experiments(new_repo: :control) + + visit new_project_path + + find('#nav-projects-dropdown').click + + page.within('#nav-projects-dropdown') do + expect(page).to have_selector('a', text: 'Create blank project') + expect(page).to have_selector('a', text: 'Import project') + expect(page).to have_no_selector('a', text: 'Create blank project/repository') + expect(page).to have_no_selector('a', text: 'Import project/repository') + end + end + + it 'when in candidate it renders "project/repository" in the new projects dropdown' do + stub_experiments(new_repo: :candidate) + + visit new_project_path + + find('#nav-projects-dropdown').click + + page.within('#nav-projects-dropdown') do + expect(page).to have_selector('a', text: 'Create blank project/repository') + expect(page).to have_selector('a', text: 'Import project/repository') + end + end + end + end + it 'shows a message if multiple levels are restricted' do Gitlab::CurrentSettings.update!( restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL] diff --git a/spec/features/projects/pages/user_edits_settings_spec.rb b/spec/features/projects/pages/user_edits_settings_spec.rb index 6156b5243de..412ba17cf20 100644 --- a/spec/features/projects/pages/user_edits_settings_spec.rb +++ b/spec/features/projects/pages/user_edits_settings_spec.rb @@ -175,7 +175,6 @@ RSpec.describe 'Pages edits pages settings', :js do expect(page).not_to have_field(:project_pages_https_only) expect(page).not_to have_content('Force HTTPS (requires valid certificates)') - expect(page).to have_button('Save') end end end diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 94800717677..4a0581bb5cf 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -566,7 +566,7 @@ RSpec.describe 'Pipeline', :js do end before do - pipeline.update(user: user) + pipeline.update!(user: user) end it 'shows the pipeline information' do @@ -628,7 +628,7 @@ RSpec.describe 'Pipeline', :js do context 'when user does not have access to read jobs' do before do - project.update(public_builds: false) + project.update!(public_builds: false) end describe 'GET /:project/-/pipelines/:id' do @@ -709,9 +709,9 @@ RSpec.describe 'Pipeline', :js do end end - it 'displays the PipelineSchedule in an active state' do + it 'displays the PipelineSchedule in an inactive state' do visit project_pipeline_schedules_path(project) - page.click_link('Active') + page.click_link('Inactive') expect(page).to have_selector('table.ci-table > tbody > tr > td', text: 'blocked user schedule') end @@ -1185,7 +1185,7 @@ RSpec.describe 'Pipeline', :js do let(:role) { :guest } before do - project.update(public_builds: false) + project.update!(public_builds: false) end context 'when accessing failed jobs page' do diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 9037aa5c9a8..e375bc10dbf 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -14,7 +14,6 @@ RSpec.describe 'Pipelines', :js do sign_in(user) stub_feature_flags(graphql_pipeline_details: false) stub_feature_flags(graphql_pipeline_details_users: false) - stub_feature_flags(new_pipelines_table: false) project.add_developer(user) project.update!(auto_devops_attributes: { enabled: false }) @@ -94,12 +93,12 @@ RSpec.describe 'Pipelines', :js do wait_for_requests end - it 'renders run pipeline link' do - expect(page).to have_link('Run Pipeline') + it 'renders "CI lint" link' do + expect(page).to have_link('CI lint') end - it 'renders ci lint link' do - expect(page).to have_link('CI Lint') + it 'renders "Run pipeline" link' do + expect(page).to have_link('Run pipeline') end end @@ -534,7 +533,7 @@ RSpec.describe 'Pipelines', :js do end it 'renders a mini pipeline graph' do - expect(page).to have_selector('[data-testid="widget-mini-pipeline-graph"]') + expect(page).to have_selector('[data-testid="pipeline-mini-graph"]') expect(page).to have_selector(dropdown_selector) end @@ -677,7 +676,7 @@ RSpec.describe 'Pipelines', :js do end it 'creates a new pipeline' do - expect { click_on 'Run Pipeline' } + expect { click_on 'Run pipeline' } .to change { Ci::Pipeline.count }.by(1) expect(Ci::Pipeline.last).to be_web @@ -690,7 +689,7 @@ RSpec.describe 'Pipelines', :js do fill_in "Input variable value", with: "value" end - expect { click_on 'Run Pipeline' } + expect { click_on 'Run pipeline' } .to change { Ci::Pipeline.count }.by(1) expect(Ci::Pipeline.last.variables.map { |var| var.slice(:key, :secret_value) }) @@ -701,7 +700,7 @@ RSpec.describe 'Pipelines', :js do context 'without gitlab-ci.yml' do before do - click_on 'Run Pipeline' + click_on 'Run pipeline' end it { expect(page).to have_content('Missing CI config file') } @@ -714,44 +713,13 @@ RSpec.describe 'Pipelines', :js do click_link 'master' end - expect { click_on 'Run Pipeline' } + expect { click_on 'Run pipeline' } .to change { Ci::Pipeline.count }.by(1) end end end end - describe 'Run Pipelines' do - let(:project) { create(:project, :repository) } - - before do - stub_feature_flags(new_pipeline_form: false) - visit new_project_pipeline_path(project) - end - - describe 'new pipeline page' do - it 'has field to add a new pipeline' do - expect(page).to have_selector('.js-branch-select') - expect(find('.js-branch-select')).to have_content project.default_branch - expect(page).to have_content('Run for') - end - end - - describe 'find pipelines' do - it 'shows filtered pipelines', :js do - click_button project.default_branch - - page.within '.dropdown-menu' do - find('.dropdown-input-field').native.send_keys('fix') - - page.within '.dropdown-content' do - expect(page).to have_content('fix') - end - end - end - end - end - describe 'Reset runner caches' do let(:project) { create(:project, :repository) } @@ -762,17 +730,17 @@ RSpec.describe 'Pipelines', :js do end it 'has a clear caches button' do - expect(page).to have_button 'Clear Runner Caches' + 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) + project.update!(jobs_cache_index: 1) end it 'increments jobs_cache_index' do - click_button 'Clear Runner Caches' + click_button 'Clear runner caches' wait_for_requests expect(page.find('.flash-notice')).to have_content 'Project cache successfully reset.' end @@ -780,7 +748,7 @@ RSpec.describe 'Pipelines', :js do context 'when project does not have jobs_cache_index' do it 'sets jobs_cache_index to 1' do - click_button 'Clear Runner Caches' + click_button 'Clear runner caches' wait_for_requests expect(page.find('.flash-notice')).to have_content 'Project cache successfully reset.' end @@ -788,6 +756,37 @@ RSpec.describe 'Pipelines', :js do end end + describe 'Run Pipelines' do + let(:project) { create(:project, :repository) } + + before do + stub_feature_flags(new_pipeline_form: false) + visit new_project_pipeline_path(project) + end + + describe 'new pipeline page' do + it 'has field to add a new pipeline' do + expect(page).to have_selector('.js-branch-select') + expect(find('.js-branch-select')).to have_content project.default_branch + expect(page).to have_content('Run for') + end + end + + describe 'find pipelines' do + it 'shows filtered pipelines', :js do + click_button project.default_branch + + page.within '.dropdown-menu' do + find('.dropdown-input-field').native.send_keys('fix') + + page.within '.dropdown-content' do + expect(page).to have_content('fix') + end + end + end + end + end + describe 'Empty State' do let(:project) { create(:project, :repository) } diff --git a/spec/features/projects/releases/user_views_edit_release_spec.rb b/spec/features/projects/releases/user_views_edit_release_spec.rb index bb54b6be9c4..024c0a227c5 100644 --- a/spec/features/projects/releases/user_views_edit_release_spec.rb +++ b/spec/features/projects/releases/user_views_edit_release_spec.rb @@ -37,7 +37,7 @@ RSpec.describe 'User edits Release', :js do end it 'renders the edit Release form' do - expect(page).to have_content('Releases are based on Git tags. We recommend tags that use semantic versioning, for example v1.0, v2.0-pre.') + expect(page).to have_content('Releases are based on Git tags. We recommend tags that use semantic versioning, for example v1.0.0, v2.1.0-pre.') expect(find_field('Tag name', disabled: true).value).to eq(release.tag) expect(find_field('Release title').value).to eq(release.name) diff --git a/spec/features/projects/releases/user_views_release_spec.rb b/spec/features/projects/releases/user_views_release_spec.rb index 186122536ce..4410f345e56 100644 --- a/spec/features/projects/releases/user_views_release_spec.rb +++ b/spec/features/projects/releases/user_views_release_spec.rb @@ -5,7 +5,6 @@ require 'spec_helper' RSpec.describe 'User views Release', :js do let(:project) { create(:project, :repository) } let(:user) { create(:user) } - let(:graphql_feature_flag) { true } let(:release) do create(:release, @@ -15,8 +14,6 @@ RSpec.describe 'User views Release', :js do end before do - stub_feature_flags(graphql_individual_release_page: graphql_feature_flag) - project.add_developer(user) sign_in(user) @@ -26,35 +23,23 @@ RSpec.describe 'User views Release', :js do it_behaves_like 'page meta description', 'Lorem ipsum dolor sit amet' - shared_examples 'release page' do - it 'renders the breadcrumbs' do - within('.breadcrumbs') do - expect(page).to have_content("#{project.creator.name} #{project.name} Releases #{release.name}") - - expect(page).to have_link(project.creator.name, href: user_path(project.creator)) - expect(page).to have_link(project.name, href: project_path(project)) - expect(page).to have_link('Releases', href: project_releases_path(project)) - expect(page).to have_link(release.name, href: project_release_path(project, release)) - end - end + it 'renders the breadcrumbs' do + within('.breadcrumbs') do + expect(page).to have_content("#{project.creator.name} #{project.name} Releases #{release.name}") - it 'renders the release details' do - within('.release-block') do - expect(page).to have_content(release.name) - expect(page).to have_content(release.tag) - expect(page).to have_content(release.commit.short_id) - expect(page).to have_content('Lorem ipsum dolor sit amet') - end + expect(page).to have_link(project.creator.name, href: user_path(project.creator)) + expect(page).to have_link(project.name, href: project_path(project)) + expect(page).to have_link('Releases', href: project_releases_path(project)) + expect(page).to have_link(release.name, href: project_release_path(project, release)) end end - describe 'when the graphql_individual_release_page feature flag is enabled' do - it_behaves_like 'release page' - end - - describe 'when the graphql_individual_release_page feature flag is disabled' do - let(:graphql_feature_flag) { false } - - it_behaves_like 'release page' + it 'renders the release details' do + within('.release-block') do + expect(page).to have_content(release.name) + expect(page).to have_content(release.tag) + expect(page).to have_content(release.commit.short_id) + expect(page).to have_content('Lorem ipsum dolor sit amet') + end end end diff --git a/spec/features/projects/remote_mirror_spec.rb b/spec/features/projects/remote_mirror_spec.rb index 26d27c914cc..7bbffe627f6 100644 --- a/spec/features/projects/remote_mirror_spec.rb +++ b/spec/features/projects/remote_mirror_spec.rb @@ -15,7 +15,7 @@ RSpec.describe 'Project remote mirror', :feature do context 'when last_error is present but last_update_at is not' do it 'renders error message without timstamp' do - remote_mirror.update(last_error: 'Some new error', last_update_at: nil) + remote_mirror.update!(last_error: 'Some new error', last_update_at: nil) visit project_mirror_path(project) @@ -25,7 +25,7 @@ RSpec.describe 'Project remote mirror', :feature do context 'when last_error and last_update_at are present' do it 'renders error message with timestamp' do - remote_mirror.update(last_error: 'Some new error', last_update_at: Time.now - 5.minutes) + remote_mirror.update!(last_error: 'Some new error', last_update_at: Time.now - 5.minutes) visit project_mirror_path(project) diff --git a/spec/features/projects/services/disable_triggers_spec.rb b/spec/features/projects/services/disable_triggers_spec.rb index b3a3d7f0622..d9e200cf563 100644 --- a/spec/features/projects/services/disable_triggers_spec.rb +++ b/spec/features/projects/services/disable_triggers_spec.rb @@ -12,10 +12,10 @@ RSpec.describe 'Disable individual triggers', :js do end context 'service has multiple supported events' do - let(:service_name) { 'HipChat' } + let(:service_name) { 'Jenkins' } it 'shows trigger checkboxes' do - event_count = HipchatService.supported_events.count + event_count = JenkinsService.supported_events.count expect(page).to have_content "Trigger" expect(page).to have_css(checkbox_selector, visible: :all, count: event_count) diff --git a/spec/features/projects/services/user_activates_asana_spec.rb b/spec/features/projects/services/user_activates_asana_spec.rb index e95e7e89fc2..cf2290383e8 100644 --- a/spec/features/projects/services/user_activates_asana_spec.rb +++ b/spec/features/projects/services/user_activates_asana_spec.rb @@ -7,7 +7,7 @@ RSpec.describe 'User activates Asana' do it 'activates service', :js do visit_project_integration('Asana') - fill_in('Api key', with: 'verySecret') + fill_in('API key', with: 'verySecret') fill_in('Restrict to branch', with: 'verySecret') click_test_then_save_integration diff --git a/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb b/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb index a9d91454670..91db375be3a 100644 --- a/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb +++ b/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb @@ -10,19 +10,20 @@ RSpec.describe 'User activates Atlassian Bamboo CI' do end it 'activates service', :js do - visit_project_integration('Atlassian Bamboo CI') - fill_in('Bamboo url', with: 'http://bamboo.example.com') + visit_project_integration('Atlassian Bamboo') + fill_in('Bamboo URL', with: 'http://bamboo.example.com') fill_in('Build key', with: 'KEY') fill_in('Username', with: 'user') fill_in('Password', with: 'verySecret') click_test_then_save_integration(expect_test_to_fail: false) - expect(page).to have_content('Atlassian Bamboo CI settings saved and active.') + expect(page).to have_content('Atlassian Bamboo settings saved and active.') # Password field should not be filled in. - click_link('Atlassian Bamboo CI') + click_link('Atlassian Bamboo') - expect(find_field('Enter new Password').value).to be_blank + expect(find_field('Enter new password').value).to be_blank + expect(page).to have_content('Leave blank to use your current password') end end diff --git a/spec/features/projects/services/user_activates_hipchat_spec.rb b/spec/features/projects/services/user_activates_hipchat_spec.rb deleted file mode 100644 index cffb780e05d..00000000000 --- a/spec/features/projects/services/user_activates_hipchat_spec.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'User activates HipChat', :js do - include_context 'project service activation' - - context 'with standard settings' do - before do - stub_request(:post, /.*api.hipchat.com.*/) - end - - it 'activates service' do - visit_project_integration('HipChat') - fill_in('Room', with: 'gitlab') - fill_in('Token', with: 'verySecret') - - click_test_then_save_integration(expect_test_to_fail: false) - - expect(page).to have_content('HipChat settings saved and active.') - end - end - - context 'with custom settings' do - before do - stub_request(:post, /.*chat.example.com.*/) - end - - it 'activates service' do - visit_project_integration('HipChat') - fill_in('Room', with: 'gitlab_custom') - fill_in('Token', with: 'secretCustom') - fill_in('Server', with: 'https://chat.example.com') - - click_test_then_save_integration(expect_test_to_fail: false) - - expect(page).to have_content('HipChat settings saved and active.') - end - end -end diff --git a/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb b/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb index 72881054c6c..17bfe8fc1e2 100644 --- a/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb +++ b/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb @@ -10,16 +10,16 @@ RSpec.describe 'User activates JetBrains TeamCity CI' do end it 'activates service', :js do - visit_project_integration('JetBrains TeamCity CI') + visit_project_integration('JetBrains TeamCity') check('Push') check('Merge Request') - fill_in('Teamcity url', with: 'http://teamcity.example.com') + fill_in('TeamCity server URL', with: 'http://teamcity.example.com') fill_in('Build type', with: 'GitlabTest_Build') fill_in('Username', with: 'user') fill_in('Password', with: 'verySecret') click_test_then_save_integration(expect_test_to_fail: false) - expect(page).to have_content('JetBrains TeamCity CI settings saved and active.') + expect(page).to have_content('JetBrains TeamCity settings saved and active.') end end diff --git a/spec/features/projects/services/user_activates_jira_spec.rb b/spec/features/projects/services/user_activates_jira_spec.rb index 85afc54be48..10f84aae93f 100644 --- a/spec/features/projects/services/user_activates_jira_spec.rb +++ b/spec/features/projects/services/user_activates_jira_spec.rb @@ -6,12 +6,13 @@ RSpec.describe 'User activates Jira', :js do include_context 'project service activation' include_context 'project service Jira context' + before do + stub_request(:get, test_url).to_return(body: { key: 'value' }.to_json) + end + describe 'user tests Jira Service' do context 'when Jira connection test succeeds' do before do - server_info = { key: 'value' }.to_json - stub_request(:get, test_url).with(basic_auth: %w(username password)).to_return(body: server_info) - visit_project_integration('Jira') fill_form click_test_then_save_integration(expect_test_to_fail: false) @@ -81,4 +82,68 @@ RSpec.describe 'User activates Jira', :js do end end end + + describe 'issue transition settings' do + it 'using custom transitions' do + visit_project_integration('Jira') + + expect(page).to have_field('Enable Jira transitions', checked: false) + + check 'Enable Jira transitions' + + expect(page).to have_field('Move to Done', checked: true) + + fill_form + choose 'Use custom transitions' + click_save_integration + + within '[data-testid="issue-transition-mode"]' do + expect(page).to have_content('This field is required.') + end + + fill_in 'service[jira_issue_transition_id]', with: '1, 2, 3' + click_save_integration + + expect(page).to have_content('Jira settings saved and active.') + expect(project.reload.jira_service.data_fields).to have_attributes( + jira_issue_transition_automatic: false, + jira_issue_transition_id: '1, 2, 3' + ) + end + + it 'using automatic transitions' do + create(:jira_service, project: project, jira_issue_transition_automatic: false, jira_issue_transition_id: '1, 2, 3') + visit_project_integration('Jira') + + expect(page).to have_field('Enable Jira transitions', checked: true) + expect(page).to have_field('Use custom transitions', checked: true) + expect(page).to have_field('service[jira_issue_transition_id]', with: '1, 2, 3') + + choose 'Move to Done' + click_save_integration + + expect(page).to have_content('Jira settings saved and active.') + expect(project.reload.jira_service.data_fields).to have_attributes( + jira_issue_transition_automatic: true, + jira_issue_transition_id: '' + ) + end + + it 'disabling issue transitions' do + create(:jira_service, project: project, jira_issue_transition_automatic: true, jira_issue_transition_id: '1, 2, 3') + visit_project_integration('Jira') + + expect(page).to have_field('Enable Jira transitions', checked: true) + expect(page).to have_field('Move to Done', checked: true) + + uncheck 'Enable Jira transitions' + click_save_integration + + expect(page).to have_content('Jira settings saved and active.') + expect(project.reload.jira_service.data_fields).to have_attributes( + jira_issue_transition_automatic: false, + jira_issue_transition_id: '' + ) + end + end end diff --git a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb index 88812fc188b..54a501e89a2 100644 --- a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb +++ b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb @@ -15,7 +15,7 @@ RSpec.describe 'Set up Mattermost slash commands', :js do let(:mattermost_enabled) { true } it 'shows a help message' do - expect(page).to have_content("This service allows users to perform common") + expect(page).to have_content("Use this service to perform common") end it 'shows a token placeholder' do diff --git a/spec/features/projects/services/user_activates_pushover_spec.rb b/spec/features/projects/services/user_activates_pushover_spec.rb index 3cfd069032a..97003ab7c2a 100644 --- a/spec/features/projects/services/user_activates_pushover_spec.rb +++ b/spec/features/projects/services/user_activates_pushover_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'User activates Pushover' do it 'activates service', :js do visit_project_integration('Pushover') - fill_in('Api key', with: 'verySecret') + fill_in('API key', with: 'verySecret') fill_in('User key', with: 'verySecret') fill_in('Device', with: 'myDevice') select('High Priority', from: 'Priority') diff --git a/spec/features/projects/services/user_activates_slack_notifications_spec.rb b/spec/features/projects/services/user_activates_slack_notifications_spec.rb index 2a880e05e0f..0cba1ee1c4c 100644 --- a/spec/features/projects/services/user_activates_slack_notifications_spec.rb +++ b/spec/features/projects/services/user_activates_slack_notifications_spec.rb @@ -25,7 +25,7 @@ RSpec.describe 'User activates Slack notifications', :js do before do service.fields - service.update( + service.update!( push_channel: 1, issue_channel: 2, merge_request_channel: 3, diff --git a/spec/features/projects/services/user_views_services_spec.rb b/spec/features/projects/services/user_views_services_spec.rb index fef6b7bd991..b936a7f38f6 100644 --- a/spec/features/projects/services/user_views_services_spec.rb +++ b/spec/features/projects/services/user_views_services_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'User views services' do +RSpec.describe 'User views services', :js do include_context 'project service activation' it 'shows the list of available services' do @@ -10,7 +10,7 @@ RSpec.describe 'User views services' do expect(page).to have_content('Integrations') expect(page).to have_content('Campfire') - expect(page).to have_content('HipChat') + expect(page).to have_content('Jira') expect(page).to have_content('Assembla') expect(page).to have_content('Pushover') expect(page).to have_content('Atlassian Bamboo') diff --git a/spec/features/projects/settings/access_tokens_spec.rb b/spec/features/projects/settings/access_tokens_spec.rb index 45fe19deb8e..8083c851bb7 100644 --- a/spec/features/projects/settings/access_tokens_spec.rb +++ b/spec/features/projects/settings/access_tokens_spec.rb @@ -5,7 +5,8 @@ require 'spec_helper' RSpec.describe 'Project > Settings > Access Tokens', :js do let_it_be(:user) { create(:user) } let_it_be(:bot_user) { create(:user, :project_bot) } - let_it_be(:project) { create(:project) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } before_all do project.add_maintainer(user) @@ -33,6 +34,18 @@ RSpec.describe 'Project > Settings > Access Tokens', :js do find('#created-personal-access-token').value end + context 'when user is not a project maintainer' do + before do + project.add_developer(user) + end + + it 'does not show project access token page' do + visit project_settings_access_tokens_path(project) + + expect(page).to have_content("Page Not Found") + end + end + describe 'token creation' do it 'allows creation of a project access token' do name = 'My project access token' @@ -57,6 +70,81 @@ RSpec.describe 'Project > Settings > Access Tokens', :js do expect(active_project_access_tokens).to have_text('read_api') expect(created_project_access_token).not_to be_empty end + + context 'when token creation is not allowed' do + before do + group.namespace_settings.update_column(:resource_access_token_creation_allowed, false) + end + + it 'does not show project access token creation form' do + visit project_settings_access_tokens_path(project) + + expect(page).not_to have_selector('#new_project_access_token') + end + + it 'shows project access token creation disabled text' do + visit project_settings_access_tokens_path(project) + + expect(page).to have_text('Project access token creation is disabled in this group. You can still use and manage existing tokens.') + end + + context 'with a project in a personal namespace' do + let(:personal_project) { create(:project) } + + before do + personal_project.add_maintainer(user) + end + + it 'shows project access token creation form and text' do + visit project_settings_access_tokens_path(personal_project) + + expect(page).to have_selector('#new_project_access_token') + expect(page).to have_text('You can generate an access token scoped to this project for each application to use the GitLab API.') + end + end + + context 'group settings link' do + context 'when user is not a group owner' do + before do + group.add_developer(user) + end + + it 'does not show group settings link' do + visit project_settings_access_tokens_path(project) + + expect(page).not_to have_link('group settings', href: edit_group_path(group)) + end + end + + context 'with nested groups' do + let(:subgroup) { create(:group, parent: group) } + + context 'when user is not a top level group owner' do + before do + subgroup.add_owner(user) + end + + it 'does not show group settings link' do + visit project_settings_access_tokens_path(project) + + expect(page).not_to have_link('group settings', href: edit_group_path(group)) + end + end + end + + context 'when user is a group owner' do + before do + group.add_owner(user) + end + + it 'shows group settings link' do + visit project_settings_access_tokens_path(project) + + expect(page).to have_link('group settings', href: edit_group_path(group)) + end + end + end + end end describe 'active tokens' do @@ -83,11 +171,25 @@ RSpec.describe 'Project > Settings > Access Tokens', :js do end it 'removes expired tokens from active section' do - project_access_token.update(expires_at: 5.days.ago) + project_access_token.update!(expires_at: 5.days.ago) visit project_settings_access_tokens_path(project) expect(page).to have_selector('.settings-message') expect(no_project_access_tokens_message).to have_text(no_active_tokens_text) end + + context 'when resource access token creation is not allowed' do + before do + group.namespace_settings.update_column(:resource_access_token_creation_allowed, false) + end + + it 'allows revocation of an active token' do + visit project_settings_access_tokens_path(project) + accept_confirm { click_on 'Revoke' } + + expect(page).to have_selector('.settings-message') + expect(no_project_access_tokens_message).to have_text(no_active_tokens_text) + end + end end end diff --git a/spec/features/projects/settings/forked_project_settings_spec.rb b/spec/features/projects/settings/forked_project_settings_spec.rb index f6c25d483ad..a84516e19f9 100644 --- a/spec/features/projects/settings/forked_project_settings_spec.rb +++ b/spec/features/projects/settings/forked_project_settings_spec.rb @@ -15,7 +15,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do end shared_examples 'project settings for a forked projects' do - it 'allows deleting the link to the forked project' do + it 'allows deleting the link to the forked project', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/327817' do visit edit_project_path(forked_project) click_button 'Remove fork relationship' @@ -25,7 +25,8 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do fill_in('confirm_name_input', with: forked_project.name) click_button('Confirm') - expect(page).to have_content('The fork relationship has been removed.') + wait_for_requests + expect(forked_project.reload.forked?).to be_falsy end end diff --git a/spec/features/projects/settings/operations_settings_spec.rb b/spec/features/projects/settings/operations_settings_spec.rb index fe0ee52e4fa..ca976997142 100644 --- a/spec/features/projects/settings/operations_settings_spec.rb +++ b/spec/features/projects/settings/operations_settings_spec.rb @@ -146,7 +146,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do click_button('Connect') - assert_text('Connection has failed. Re-check Auth Token and try again.') + assert_text('Connection failed. Check Auth Token and try again.') end end end diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb index 6e4082d1391..bc60cdd2f8e 100644 --- a/spec/features/projects/settings/registry_settings_spec.rb +++ b/spec/features/projects/settings/registry_settings_spec.rb @@ -39,7 +39,7 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p select('7 days', from: 'Remove tags older than:') fill_in('Remove tags matching:', with: '.*-production') - submit_button = find('.btn.gl-button.btn-success') + submit_button = find('[data-testid="save-button"') expect(submit_button).not_to be_disabled submit_button.click end @@ -53,7 +53,7 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p within '#js-registry-policies' do fill_in('Remove tags matching:', with: '*-production') - submit_button = find('.btn.gl-button.btn-success') + submit_button = find('[data-testid="save-button"') expect(submit_button).not_to be_disabled submit_button.click 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 397c334a2b8..ebda5c9ff59 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 @@ -2,6 +2,8 @@ require 'spec_helper' RSpec.describe 'Projects > Settings > User manages merge request settings' do + include ProjectForksHelper + let(:user) { create(:user) } let(:project) { create(:project, :public, namespace: user.namespace, path: 'gitlab', name: 'sample') } @@ -198,4 +200,36 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do expect(project.reload.project_setting.squash_option).to eq('never') end end + + describe 'target project settings' do + context 'when project is a fork' do + let_it_be(:upstream) { create(:project, :public) } + + let(:project) { fork_project(upstream, user) } + + it 'allows to change merge request target project behavior' do + expect(page).to have_content 'The default target project for merge requests' + + radio = find_field('project_project_setting_attributes_mr_default_target_self_false') + expect(radio).to be_checked + + choose('project_project_setting_attributes_mr_default_target_self_true') + + within('.merge-request-settings-form') do + find('.rspec-save-merge-request-changes') + click_on('Save changes') + end + + find('.flash-notice') + radio = find_field('project_project_setting_attributes_mr_default_target_self_true') + + expect(radio).to be_checked + expect(project.reload.project_setting.mr_default_target_self).to be_truthy + end + end + + it 'does not show target project section' do + expect(page).not_to have_content 'The default target project for merge requests' + end + end end diff --git a/spec/features/projects/settings/user_searches_in_settings_spec.rb b/spec/features/projects/settings/user_searches_in_settings_spec.rb index 4c5b39d5282..9b09958bae5 100644 --- a/spec/features/projects/settings/user_searches_in_settings_spec.rb +++ b/spec/features/projects/settings/user_searches_in_settings_spec.rb @@ -4,16 +4,42 @@ require 'spec_helper' RSpec.describe 'User searches project settings', :js do let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:project, :repository, namespace: user.namespace) } + let_it_be(:project) { create(:project, :repository, namespace: user.namespace, pages_https_only: false) } before do sign_in(user) end context 'in general settings page' do - let(:visit_path) { edit_project_path(project) } + before do + visit edit_project_path(project) + end + + it_behaves_like 'can search settings', 'Naming', 'Visibility' + end + + context 'in Integrations page' do + before do + visit project_settings_integrations_path(project) + end + + it_behaves_like 'can highlight results', 'third-party applications' + end + + context 'in Webhooks page' do + before do + visit project_hooks_path(project) + end - it_behaves_like 'can search settings with feature flag check', 'Naming', 'Visibility' + it_behaves_like 'can highlight results', 'Secret token' + end + + context 'in Access Tokens page' do + before do + visit project_settings_access_tokens_path(project) + end + + it_behaves_like 'can highlight results', 'Expires at' end context 'in Repository page' do @@ -37,6 +63,16 @@ RSpec.describe 'User searches project settings', :js do visit project_settings_operations_path(project) end - it_behaves_like 'can search settings', 'Alerts', 'Error tracking' + it_behaves_like 'can search settings', 'Alert integrations', 'Error tracking' + end + + context 'in Pages page' do + before do + allow(Gitlab.config.pages).to receive(:enabled).and_return(true) + + visit project_pages_path(project) + end + + it_behaves_like 'can highlight results', 'static website' end end diff --git a/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb b/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb index d0f297d2067..eed3494ef5b 100644 --- a/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb +++ b/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb @@ -13,16 +13,10 @@ RSpec.describe 'Repository Settings > User sees revoke deploy token modal', :js sign_in(user) stub_feature_flags(ajax_new_deploy_token: project) visit(project_settings_repository_path(project)) - click_link('Revoke') + click_button('Revoke') end it 'shows the revoke deploy token modal' do expect(page).to have_content('You are about to revoke') end - - it 'closes the revoke deploy token modal with escape keypress' do - find('.modal.show').send_keys(:escape) - - expect(page).not_to have_content('You are about to revoke') - end end diff --git a/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb b/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb index 5e878411f6a..b7af0c29b33 100644 --- a/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb +++ b/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb @@ -10,7 +10,7 @@ RSpec.describe 'Projects > Show > User sees a deletion failure message' do end it 'shows error message if deletion for project fails' do - project.update(delete_error: "Something went wrong", pending_delete: false) + project.update!(delete_error: "Something went wrong", pending_delete: false) visit project_path(project) diff --git a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb index 9b51e867156..dc551158895 100644 --- a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb +++ b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb @@ -226,11 +226,11 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do expect(project.repository.gitlab_ci_yml).to be_nil page.within('.project-buttons') do - expect(page).to have_link('Set up CI/CD', href: presenter.add_ci_yml_path) + expect(page).to have_link('Set up CI/CD', href: project_ci_pipeline_editor_path(project)) end end - it 'no "Set up CI/CD" button if the project already has a .gitlab-ci.yml' do + it '"Set up CI/CD" button is renamed if the project already has a .gitlab-ci.yml' do Files::CreateService.new( project, project.creator, @@ -247,6 +247,7 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do page.within('.project-buttons') do expect(page).not_to have_link('Set up CI/CD') + expect(page).to have_link('CI/CD configuration') end end end diff --git a/spec/features/projects/show/user_uploads_files_spec.rb b/spec/features/projects/show/user_uploads_files_spec.rb index 2030c4d998a..eb230082bfa 100644 --- a/spec/features/projects/show/user_uploads_files_spec.rb +++ b/spec/features/projects/show/user_uploads_files_spec.rb @@ -17,11 +17,17 @@ RSpec.describe 'Projects > Show > User uploads files' do context 'when a user has write access' do before do visit(project_path(project)) + + wait_for_requests end - include_examples 'it uploads and commit a new text file' + include_examples 'it uploads and commits a new text file' + + include_examples 'it uploads and commits a new image file' + + include_examples 'it uploads and commits a new pdf file' - include_examples 'it uploads and commit a new image file' + include_examples 'it uploads a file to a sub-directory' end context 'when a user does not have write access' do @@ -31,7 +37,7 @@ RSpec.describe 'Projects > Show > User uploads files' do visit(project_path(project2)) end - include_examples 'it uploads and commit a new file to a forked project' + include_examples 'it uploads and commits a new file to a forked project' end context 'when in the empty_repo_upload experiment' do diff --git a/spec/features/projects/snippets/user_comments_on_snippet_spec.rb b/spec/features/projects/snippets/user_comments_on_snippet_spec.rb index b37d40c0eed..3ccb73c88ef 100644 --- a/spec/features/projects/snippets/user_comments_on_snippet_spec.rb +++ b/spec/features/projects/snippets/user_comments_on_snippet_spec.rb @@ -29,7 +29,6 @@ RSpec.describe 'Projects > Snippets > User comments on a snippet', :js do end it 'has autocomplete' do - find('#note_note').native.send_keys('') fill_in 'note[note]', with: '@' expect(page).to have_selector('.atwho-view') diff --git a/spec/features/projects/sub_group_issuables_spec.rb b/spec/features/projects/sub_group_issuables_spec.rb index 8c1d88276df..d7614201740 100644 --- a/spec/features/projects/sub_group_issuables_spec.rb +++ b/spec/features/projects/sub_group_issuables_spec.rb @@ -16,18 +16,18 @@ RSpec.describe 'Subgroup Issuables', :js do it 'shows the full subgroup title when issues index page is empty' do visit project_issues_path(project) - expect_to_have_full_subgroup_title + expect_to_have_breadcrumb_links end it 'shows the full subgroup title when merge requests index page is empty' do visit project_merge_requests_path(project) - expect_to_have_full_subgroup_title + expect_to_have_breadcrumb_links end - def expect_to_have_full_subgroup_title - title = find('.breadcrumbs-links') + def expect_to_have_breadcrumb_links + links = find('[data-testid="breadcrumb-links"]') - expect(title).to have_content 'group subgroup project' + expect(links).to have_content 'group subgroup project' end end diff --git a/spec/features/projects/user_sees_sidebar_spec.rb b/spec/features/projects/user_sees_sidebar_spec.rb index e5ba6b503cc..ff6217d02a7 100644 --- a/spec/features/projects/user_sees_sidebar_spec.rb +++ b/spec/features/projects/user_sees_sidebar_spec.rb @@ -208,7 +208,7 @@ RSpec.describe 'Projects > User sees sidebar' do it 'shows build tab if builds are public' do project.public_builds = true - project.save + project.save! visit project_path(project) diff --git a/spec/features/projects/user_sees_user_popover_spec.rb b/spec/features/projects/user_sees_user_popover_spec.rb index 52e65deae3b..e357824a533 100644 --- a/spec/features/projects/user_sees_user_popover_spec.rb +++ b/spec/features/projects/user_sees_user_popover_spec.rb @@ -35,7 +35,7 @@ RSpec.describe 'User sees user popover', :js do end end - it "displays user popover in system note" do + it 'displays user popover in system note', :sidekiq_inline do add_note("/assign @#{user.username}") find('.system-note-message .js-user-link').hover diff --git a/spec/features/projects/user_uses_shortcuts_spec.rb b/spec/features/projects/user_uses_shortcuts_spec.rb index f97c8d820e3..b6fde19e0d4 100644 --- a/spec/features/projects/user_uses_shortcuts_spec.rb +++ b/spec/features/projects/user_uses_shortcuts_spec.rb @@ -151,7 +151,7 @@ RSpec.describe 'User uses shortcuts', :js do find('body').native.send_key('g') find('body').native.send_key('m') - expect(page).to have_active_navigation('Merge Requests') + expect(page).to have_active_navigation('Merge requests') end end diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 4730679feb8..c18b0f2688b 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -174,26 +174,6 @@ RSpec.describe 'Project' do end end - describe 'remove forked relationship', :js do - let(:user) { create(:user) } - let(:project) { fork_project(create(:project, :public), user, namespace: user.namespace) } - - before do - sign_in user - visit edit_project_path(project) - end - - it 'removes fork' do - expect(page).to have_content 'Remove fork relationship' - - remove_with_confirm('Remove fork relationship', project.path) - - expect(page).to have_content 'The fork relationship has been removed.' - expect(project.reload.forked?).to be_falsey - expect(page).not_to have_content 'Remove fork relationship' - end - end - describe 'showing information about source of a project fork' do let(:user) { create(:user) } let(:base_project) { create(:project, :public, :repository) } diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb index eb099359df9..207b74c990a 100644 --- a/spec/features/protected_branches_spec.rb +++ b/spec/features/protected_branches_spec.rb @@ -24,8 +24,8 @@ RSpec.describe 'Protected Branches', :js do it 'does not allow developer to removes protected branch' do visit project_branches_path(project) - fill_in 'branch-search', with: 'fix' - find('#branch-search').native.send_keys(:enter) + find('input[data-testid="branch-search"]').set('fix') + find('input[data-testid="branch-search"]').native.send_keys(:enter) expect(page).to have_css('.btn-danger.disabled') end @@ -47,8 +47,8 @@ RSpec.describe 'Protected Branches', :js do it 'removes branch after modal confirmation' do visit project_branches_path(project) - fill_in 'branch-search', with: 'fix' - find('#branch-search').native.send_keys(:enter) + find('input[data-testid="branch-search"]').set('fix') + find('input[data-testid="branch-search"]').native.send_keys(:enter) expect(page).to have_content('fix') expect(find('.all-branches')).to have_selector('li', count: 1) @@ -58,8 +58,8 @@ RSpec.describe 'Protected Branches', :js do fill_in 'delete_branch_input', with: 'fix' click_link 'Delete protected branch' - fill_in 'branch-search', with: 'fix' - find('#branch-search').native.send_keys(:enter) + find('input[data-testid="branch-search"]').set('fix') + find('input[data-testid="branch-search"]').native.send_keys(:enter) expect(page).to have_content('No branches to show') end diff --git a/spec/features/registrations/welcome_spec.rb b/spec/features/registrations/welcome_spec.rb new file mode 100644 index 00000000000..74320b69f19 --- /dev/null +++ b/spec/features/registrations/welcome_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Welcome screen' do + let(:user) { create(:user) } + + before do + gitlab_sign_in(user) + + visit users_sign_up_welcome_path + end + + it 'shows the email opt in' do + select 'Software Developer', from: 'user_role' + check 'user_email_opted_in' + click_button 'Get started!' + + expect(user.reload.email_opted_in).to eq(true) + end +end diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index cc024ab8f35..acfb7c2602a 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -49,19 +49,19 @@ RSpec.describe 'Runners' do visit project_runners_path(project) within '.activated-specific-runners' do - expect(page).to have_content('Pause') + expect(page).to have_link('Pause') end click_on 'Pause' within '.activated-specific-runners' do - expect(page).to have_content('Resume') + expect(page).to have_link('Resume') end click_on 'Resume' within '.activated-specific-runners' do - expect(page).to have_content('Pause') + expect(page).to have_link('Pause') end end @@ -79,7 +79,7 @@ RSpec.describe 'Runners' do visit project_runners_path(project) within '.activated-specific-runners' do - first('.edit-runner > a').click + first('[data-testid="edit-runner-link"]').click end expect(page.find_field('runner[access_level]')).not_to be_checked @@ -92,14 +92,14 @@ RSpec.describe 'Runners' do context 'when a runner has a tag' do before do - specific_runner.update(tag_list: ['tag']) + specific_runner.update!(tag_list: ['tag']) end it 'user edits runner not to run untagged jobs' do visit project_runners_path(project) within '.activated-specific-runners' do - first('.edit-runner > a').click + first('[data-testid="edit-runner-link"]').click end expect(page.find_field('runner[run_untagged]')).to be_checked @@ -370,7 +370,7 @@ RSpec.describe 'Runners' do context 'when a runner has a tag' do before do - runner.update(tag_list: ['tag']) + runner.update!(tag_list: ['tag']) end it 'user edits runner not to run untagged jobs' do @@ -450,7 +450,7 @@ RSpec.describe 'Runners' do context 'when a runner has a tag' do before do - runner.update(tag_list: ['tag']) + runner.update!(tag_list: ['tag']) end it 'user edits runner not to run untagged jobs' do diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb index 9296a3f33d4..4c42800cf05 100644 --- a/spec/features/search/user_uses_header_search_field_spec.rb +++ b/spec/features/search/user_uses_header_search_field_spec.rb @@ -175,7 +175,7 @@ RSpec.describe 'User uses header search field', :js do fill_in_search('Merge') within(dashboard_search_options_popup_menu) do - expect(page).to have_text('Merge Requests') + expect(page).to have_text('Merge requests') end end diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index 2440b738db3..9dcef13757a 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -356,7 +356,7 @@ RSpec.describe "Internal Project Access" do context "when allowed for public and internal" do before do - project.update(public_builds: true) + project.update!(public_builds: true) end it { is_expected.to be_allowed_for(:admin) } @@ -372,7 +372,7 @@ RSpec.describe "Internal Project Access" do context "when disallowed for public and internal" do before do - project.update(public_builds: false) + project.update!(public_builds: false) end it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) } @@ -396,7 +396,7 @@ RSpec.describe "Internal Project Access" do context "when allowed for public and internal" do before do - project.update(public_builds: true) + project.update!(public_builds: true) end it { is_expected.to be_allowed_for(:admin) } @@ -412,7 +412,7 @@ RSpec.describe "Internal Project Access" do context "when disallowed for public and internal" do before do - project.update(public_builds: false) + project.update!(public_builds: false) end it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) } @@ -436,7 +436,7 @@ RSpec.describe "Internal Project Access" do context 'when allowed for public and internal' do before do - project.update(public_builds: true) + project.update!(public_builds: true) end it { is_expected.to be_allowed_for(:admin) } @@ -452,7 +452,7 @@ RSpec.describe "Internal Project Access" do context 'when disallowed for public and internal' do before do - project.update(public_builds: false) + project.update!(public_builds: false) end it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) } diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index 9d3109b92e6..5a200bea80a 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -319,7 +319,7 @@ RSpec.describe "Private Project Access" do context 'when public builds is enabled' do before do - project.update(public_builds: true) + project.update!(public_builds: true) end it { is_expected.to be_allowed_for(:guest).of(project) } @@ -348,7 +348,7 @@ RSpec.describe "Private Project Access" do context 'when public builds is enabled' do before do - project.update(public_builds: true) + project.update!(public_builds: true) end it { is_expected.to be_allowed_for(:guest).of(project) } @@ -375,7 +375,7 @@ RSpec.describe "Private Project Access" do context 'when public builds is enabled' do before do - project.update(public_builds: true) + project.update!(public_builds: true) end it { is_expected.to be_allowed_for(:guest).of(project) } @@ -405,7 +405,7 @@ RSpec.describe "Private Project Access" do context 'when public builds is enabled' do before do - project.update(public_builds: true) + project.update!(public_builds: true) end it { is_expected.to be_allowed_for(:guest).of(project) } @@ -414,7 +414,7 @@ RSpec.describe "Private Project Access" do context 'when public buils are disabled' do before do project.public_builds = false - project.save + project.save! end it { is_expected.to be_denied_for(:guest).of(project) } @@ -440,7 +440,7 @@ RSpec.describe "Private Project Access" do context 'when public builds is enabled' do before do - project.update(public_builds: true) + project.update!(public_builds: true) end it { is_expected.to be_allowed_for(:guest).of(project) } @@ -448,7 +448,7 @@ RSpec.describe "Private Project Access" do context 'when public builds is disabled' do before do - project.update(public_builds: false) + project.update!(public_builds: false) end it { is_expected.to be_denied_for(:guest).of(project) } diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index 28a1f1cda7f..8ceb6920e77 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -164,7 +164,7 @@ RSpec.describe "Public Project Access" do context "when allowed for public" do before do - project.update(public_builds: true) + project.update!(public_builds: true) end it { is_expected.to be_allowed_for(:admin) } @@ -180,7 +180,7 @@ RSpec.describe "Public Project Access" do context "when disallowed for public" do before do - project.update(public_builds: false) + project.update!(public_builds: false) end it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) } @@ -204,7 +204,7 @@ RSpec.describe "Public Project Access" do context "when allowed for public" do before do - project.update(public_builds: true) + project.update!(public_builds: true) end it { is_expected.to be_allowed_for(:admin) } @@ -220,7 +220,7 @@ RSpec.describe "Public Project Access" do context "when disallowed for public" do before do - project.update(public_builds: false) + project.update!(public_builds: false) end it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) } @@ -244,7 +244,7 @@ RSpec.describe "Public Project Access" do context 'when allowed for public' do before do - project.update(public_builds: true) + project.update!(public_builds: true) end it { is_expected.to be_allowed_for(:admin) } @@ -260,7 +260,7 @@ RSpec.describe "Public Project Access" do context 'when disallowed for public' do before do - project.update(public_builds: false) + project.update!(public_builds: false) end it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) } diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb index ce9a2d1461e..47dad9bd88e 100644 --- a/spec/features/snippets/notes_on_personal_snippets_spec.rb +++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb @@ -108,9 +108,6 @@ RSpec.describe 'Comments on personal snippets', :js do end it 'does not have autocomplete' do - wait_for_requests - - find('#note_note').native.send_keys('') fill_in 'note[note]', with: '@' wait_for_requests diff --git a/spec/features/users/anonymous_sessions_spec.rb b/spec/features/users/anonymous_sessions_spec.rb index 420fb225f94..273d3aa346f 100644 --- a/spec/features/users/anonymous_sessions_spec.rb +++ b/spec/features/users/anonymous_sessions_spec.rb @@ -10,7 +10,7 @@ RSpec.describe 'Session TTLs', :clean_gitlab_redis_shared_state do fill_in 'user_password', with: '12345678' click_button 'Sign in' - expect(page).to have_content('Invalid Login or password') + expect(page).to have_content('Invalid login or password') expect_single_session_with_expiration(Settings.gitlab['unauthenticated_session_expire_delay']) end diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb index 1d1120709b5..e60d9d6ab69 100644 --- a/spec/features/users/login_spec.rb +++ b/spec/features/users/login_spec.rb @@ -138,7 +138,7 @@ RSpec.describe 'Login' do gitlab_sign_in(User.ghost) - expect(page).to have_content('Invalid Login or password.') + expect(page).to have_content('Invalid login or password.') end it 'does not update Devise trackable attributes', :clean_gitlab_redis_shared_state do @@ -239,7 +239,7 @@ RSpec.describe 'Login' do expect(codes.size).to eq 10 # Ensure the generated codes get saved - user.save(touch: false) + user.save!(touch: false) end context 'with valid code' do @@ -406,7 +406,7 @@ RSpec.describe 'Login' do gitlab_sign_in(user) - expect(page).to have_content('Invalid Login or password.') + expect(page).to have_content('Invalid login or password.') end end end diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb index a8372800700..56d2aaea203 100644 --- a/spec/features/users/show_spec.rb +++ b/spec/features/users/show_spec.rb @@ -33,7 +33,7 @@ RSpec.describe 'User page' do context 'work information' do it 'shows job title and organization details' do - user.update(organization: 'GitLab - work info test', job_title: 'Frontend Engineer') + user.update!(organization: 'GitLab - work info test', job_title: 'Frontend Engineer') subject @@ -41,7 +41,7 @@ RSpec.describe 'User page' do end it 'shows job title' do - user.update(organization: nil, job_title: 'Frontend Engineer - work info test') + user.update!(organization: nil, job_title: 'Frontend Engineer - work info test') subject @@ -49,7 +49,7 @@ RSpec.describe 'User page' do end it 'shows organization details' do - user.update(organization: 'GitLab - work info test', job_title: '') + user.update!(organization: 'GitLab - work info test', job_title: '') subject diff --git a/spec/features/users/terms_spec.rb b/spec/features/users/terms_spec.rb index 7500f2fe59a..8ba79d77c22 100644 --- a/spec/features/users/terms_spec.rb +++ b/spec/features/users/terms_spec.rb @@ -121,7 +121,7 @@ RSpec.describe 'Users > Terms' do enforce_terms - click_button 'Submit issue' + click_button 'Create issue' expect(current_path).to eq(terms_path) diff --git a/spec/features/whats_new_spec.rb b/spec/features/whats_new_spec.rb index 7c5625486f5..55b96361f03 100644 --- a/spec/features/whats_new_spec.rb +++ b/spec/features/whats_new_spec.rb @@ -2,34 +2,60 @@ require "spec_helper" -RSpec.describe "renders a `whats new` dropdown item", :js do +RSpec.describe "renders a `whats new` dropdown item" do let_it_be(:user) { create(:user) } - before do - sign_in(user) - end + context 'when not logged in' do + it 'and on .com it renders' do + allow(Gitlab).to receive(:com?).and_return(true) - it 'shows notification dot and count and removes it once viewed' do - visit root_dashboard_path + visit user_path(user) - page.within '.header-help' do - expect(page).to have_selector('.notification-dot', visible: true) + page.within '.header-help' do + find('.header-help-dropdown-toggle').click - find('.header-help-dropdown-toggle').click + expect(page).to have_button(text: "What's new") + end + end + + it "doesn't render what's new" do + visit user_path(user) - expect(page).to have_button(text: "What's new") - expect(page).to have_selector('.js-whats-new-notification-count') + page.within '.header-help' do + find('.header-help-dropdown-toggle').click + + expect(page).not_to have_button(text: "What's new") + end + end + end - find('button', text: "What's new").click + context 'when logged in', :js do + before do + sign_in(user) end - find('.whats-new-drawer .gl-drawer-close-button').click - find('.header-help-dropdown-toggle').click + it 'shows notification dot and count and removes it once viewed' do + visit root_dashboard_path + + page.within '.header-help' do + expect(page).to have_selector('.notification-dot', visible: true) + + find('.header-help-dropdown-toggle').click + + expect(page).to have_button(text: "What's new") + expect(page).to have_selector('.js-whats-new-notification-count') + + find('button', text: "What's new").click + end + + find('.whats-new-drawer .gl-drawer-close-button').click + find('.header-help-dropdown-toggle').click - page.within '.header-help' do - expect(page).not_to have_selector('.notification-dot', visible: true) - expect(page).to have_button(text: "What's new") - expect(page).not_to have_selector('.js-whats-new-notification-count') + page.within '.header-help' do + expect(page).not_to have_selector('.notification-dot', visible: true) + expect(page).to have_button(text: "What's new") + expect(page).not_to have_selector('.js-whats-new-notification-count') + end end end end |