diff options
Diffstat (limited to 'spec/features')
85 files changed, 2590 insertions, 1867 deletions
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index b96762ec6ad..cd148642b90 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -90,7 +90,7 @@ RSpec.describe 'Admin Appearance' do sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) visit new_project_path - find('[data-qa-panel-name="blank_project"]').click + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage expect_custom_new_project_appearance(appearance) end diff --git a/spec/features/admin/admin_dev_ops_report_spec.rb b/spec/features/admin/admin_dev_ops_report_spec.rb index 8f1960b681c..cee79f8f440 100644 --- a/spec/features/admin/admin_dev_ops_report_spec.rb +++ b/spec/features/admin/admin_dev_ops_report_spec.rb @@ -19,7 +19,9 @@ RSpec.describe 'DevOps Report page', :js do expect(page).to have_content 'Introducing Your DevOps Report' - find('.js-close-callout').click + page.within(find('[data-testid="devops-score-container"]')) do + find('[data-testid="close-icon"]').click + end expect(page).not_to have_content 'Introducing Your DevOps Report' end diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 7d7b2baf941..8315b8f44b0 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -256,7 +256,7 @@ RSpec.describe 'Admin Groups' do visit group_group_members_path(group) - page.within '[data-qa-selector="members_list"]' do + page.within '[data-qa-selector="members_list"]' do # rubocop:disable QA/SelectorUsage expect(page).to have_content(current_user.name) expect(page).to have_content('Developer') end @@ -265,7 +265,7 @@ RSpec.describe 'Admin Groups' do visit group_group_members_path(group) - page.within '[data-qa-selector="members_list"]' do + page.within '[data-qa-selector="members_list"]' do # rubocop:disable QA/SelectorUsage expect(page).not_to have_content(current_user.name) expect(page).not_to have_content('Developer') end diff --git a/spec/features/admin/admin_manage_applications_spec.rb b/spec/features/admin/admin_manage_applications_spec.rb index e54837ede11..b6437fce540 100644 --- a/spec/features/admin/admin_manage_applications_spec.rb +++ b/spec/features/admin/admin_manage_applications_spec.rb @@ -3,62 +3,14 @@ require 'spec_helper' RSpec.describe 'admin manage applications' do + let_it_be(:new_application_path) { new_admin_application_path } + let_it_be(:applications_path) { admin_applications_path } + before do admin = create(:admin) sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) end - it 'creates new oauth application' do - visit admin_applications_path - - click_on 'New application' - expect(page).to have_content('New application') - - fill_in :doorkeeper_application_name, with: 'test' - fill_in :doorkeeper_application_redirect_uri, with: 'https://test.com' - check :doorkeeper_application_trusted - check :doorkeeper_application_scopes_read_user - click_on 'Submit' - expect(page).to have_content('Application: test') - expect(page).to have_content('Application ID') - expect(page).to have_content('Secret') - expect(page).to have_content('Trusted Y') - expect(page).to have_content('Confidential Y') - - click_on 'Edit' - expect(page).to have_content('Edit application') - - fill_in :doorkeeper_application_name, with: 'test_changed' - uncheck :doorkeeper_application_trusted - uncheck :doorkeeper_application_confidential - - click_on 'Submit' - expect(page).to have_content('test_changed') - expect(page).to have_content('Application ID') - expect(page).to have_content('Secret') - expect(page).to have_content('Trusted N') - expect(page).to have_content('Confidential N') - - visit admin_applications_path - page.within '.oauth-applications' do - click_on 'Destroy' - end - expect(page.find('.oauth-applications')).not_to have_content('test_changed') - end - - context 'when scopes are blank' do - it 'returns an error' do - visit admin_applications_path - - click_on 'New application' - expect(page).to have_content('New application') - - fill_in :doorkeeper_application_name, with: 'test' - fill_in :doorkeeper_application_redirect_uri, with: 'https://test.com' - click_on 'Submit' - - expect(page).to have_content("Scopes can't be blank") - end - end + include_examples 'manage applications' end diff --git a/spec/features/admin/admin_mode/logout_spec.rb b/spec/features/admin/admin_mode/logout_spec.rb index efb4baa8164..58bea5c4b5f 100644 --- a/spec/features/admin/admin_mode/logout_spec.rb +++ b/spec/features/admin/admin_mode/logout_spec.rb @@ -9,69 +9,47 @@ RSpec.describe 'Admin Mode Logout', :js do let(:user) { create(:admin) } - shared_examples 'combined_menu: feature flag examples' do - before do - # TODO: This used to use gitlab_sign_in, instead of sign_in, but that is buggy. See - # this issue to look into why: https://gitlab.com/gitlab-org/gitlab/-/issues/331851 - sign_in(user) - gitlab_enable_admin_mode_sign_in(user) - visit admin_root_path - end + before do + # TODO: This used to use gitlab_sign_in, instead of sign_in, but that is buggy. See + # this issue to look into why: https://gitlab.com/gitlab-org/gitlab/-/issues/331851 + sign_in(user) + gitlab_enable_admin_mode_sign_in(user) + visit admin_root_path + end - it 'disable removes admin mode and redirects to root page' do - gitlab_disable_admin_mode + it 'disable removes admin mode and redirects to root page' do + gitlab_disable_admin_mode - expect(current_path).to eq root_path + expect(current_path).to eq root_path - open_top_nav + open_top_nav - within_top_nav do - expect(page).to have_link(href: new_admin_session_path) - end - end - - it 'disable shows flash notice' do - gitlab_disable_admin_mode - - expect(page).to have_selector('.flash-notice') + within_top_nav do + expect(page).to have_link(href: new_admin_session_path) end + end - context 'on a read-only instance' do - before do - allow(Gitlab::Database).to receive(:read_only?).and_return(true) - end - - it 'disable removes admin mode and redirects to root page' do - gitlab_disable_admin_mode - - expect(current_path).to eq root_path - - open_top_nav + it 'disable shows flash notice' do + gitlab_disable_admin_mode - within_top_nav do - expect(page).to have_link(href: new_admin_session_path) - end - end - end + expect(page).to have_selector('.flash-notice') end - context 'with combined_menu feature flag on' do - let(:needs_rewrite_for_combined_menu_flag_on) { true } - + context 'on a read-only instance' do before do - stub_feature_flags(combined_menu: true) + allow(Gitlab::Database).to receive(:read_only?).and_return(true) end - it_behaves_like 'combined_menu: feature flag examples' - end + it 'disable removes admin mode and redirects to root page' do + gitlab_disable_admin_mode - context 'with combined_menu feature flag off' do - let(:needs_rewrite_for_combined_menu_flag_on) { false } + expect(current_path).to eq root_path - before do - stub_feature_flags(combined_menu: false) - end + open_top_nav - it_behaves_like 'combined_menu: feature flag examples' + within_top_nav do + expect(page).to have_link(href: new_admin_session_path) + end + end end end diff --git a/spec/features/admin/admin_mode_spec.rb b/spec/features/admin/admin_mode_spec.rb index 9fd83f4af6d..24a10d3677d 100644 --- a/spec/features/admin/admin_mode_spec.rb +++ b/spec/features/admin/admin_mode_spec.rb @@ -2,48 +2,62 @@ require 'spec_helper' -RSpec.describe 'Admin mode' do +RSpec.describe 'Admin mode', :js do include MobileHelpers include Spec::Support::Helpers::Features::TopNavSpecHelpers include StubENV let(:admin) { create(:admin) } - shared_examples 'combined_menu: feature flag examples' do + before do + stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') + end + + context 'application setting :admin_mode is enabled', :request_store do before do - stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') + sign_in(admin) end - context 'application setting :admin_mode is enabled', :request_store do - before do - sign_in(admin) + context 'when not in admin mode' do + it 'has no leave admin mode button' do + visit new_admin_session_path + open_top_nav + + page.within('.navbar-sub-nav') do + expect(page).not_to have_link(href: destroy_admin_session_path) + end end - context 'when not in admin mode' do - it 'has no leave admin mode button' do - visit new_admin_session_path - open_top_nav + it 'can open pages not in admin scope' do + visit new_admin_session_path + open_top_nav_projects - page.within('.navbar-sub-nav') do - expect(page).not_to have_link(href: destroy_admin_session_path) - end + within_top_nav do + click_link('Your projects') end - it 'can open pages not in admin scope' do - visit new_admin_session_path - open_top_nav_projects + expect(page).to have_current_path(dashboard_projects_path) + end - within_top_nav do - click_link('Your projects') - end + it 'is necessary to provide credentials again before opening pages in admin scope' do + visit general_admin_application_settings_path # admin logged out because not in admin_mode - expect(page).to have_current_path(dashboard_projects_path) - end + expect(page).to have_current_path(new_admin_session_path) + end + + it 'can enter admin mode' do + visit new_admin_session_path - it 'is necessary to provide credentials again before opening pages in admin scope' do - visit general_admin_application_settings_path # admin logged out because not in admin_mode + fill_in 'user_password', with: admin.password - expect(page).to have_current_path(new_admin_session_path) + click_button 'Enter Admin Mode' + + expect(page).to have_current_path(admin_root_path) + end + + context 'on a read-only instance' do + before do + allow(Gitlab::Database).to receive(:read_only?).and_return(true) end it 'can enter admin mode' do @@ -55,151 +69,82 @@ RSpec.describe 'Admin mode' do expect(page).to have_current_path(admin_root_path) end + end + end - context 'on a read-only instance' do - before do - allow(Gitlab::Database).to receive(:read_only?).and_return(true) - end - - it 'can enter admin mode' do - visit new_admin_session_path - - fill_in 'user_password', with: admin.password + context 'when in admin_mode' do + before do + gitlab_enable_admin_mode_sign_in(admin) + end - click_button 'Enter Admin Mode' + it 'contains link to leave admin mode' do + open_top_nav - expect(page).to have_current_path(admin_root_path) - end + within_top_nav do + expect(page).to have_link(href: destroy_admin_session_path) end end - context 'when in admin_mode' do - before do - gitlab_enable_admin_mode_sign_in(admin) - end + it 'can leave admin mode using main dashboard link' do + gitlab_disable_admin_mode - it 'contains link to leave admin mode' do - open_top_nav + open_top_nav - within_top_nav do - expect(page).to have_link(href: destroy_admin_session_path) - end + within_top_nav do + expect(page).to have_link(href: new_admin_session_path) end + end - it 'can leave admin mode using main dashboard link', :js do - gitlab_disable_admin_mode - - open_top_nav + it 'can open pages not in admin scope' do + open_top_nav_projects - within_top_nav do - expect(page).to have_link(href: new_admin_session_path) - end + within_top_nav do + click_link('Your projects') end - it 'can leave admin mode using dropdown menu on smaller screens', :js do - skip('pending responsive development under :combined_menu feature flag') if Feature.enabled?(:combined_menu, default_enabled: :yaml) + expect(page).to have_current_path(dashboard_projects_path) + end - resize_screen_xs + context 'nav bar' do + it 'shows admin dashboard links on bigger screen' do visit root_dashboard_path - - find('.header-more').click unless Feature.enabled?(:combined_menu, default_enabled: :yaml) - - gitlab_disable_admin_mode - open_top_nav - find('.header-more').click unless Feature.enabled?(:combined_menu, default_enabled: :yaml) - expect(page).to have_link(href: new_admin_session_path) + expect(page).to have_link(text: 'Admin', href: admin_root_path, visible: true) + expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true) end + end - it 'can open pages not in admin scope' do - open_top_nav_projects - - within_top_nav do - click_link('Your projects') - end - - expect(page).to have_current_path(dashboard_projects_path) - end - - context 'nav bar' do - it 'shows admin dashboard links on bigger screen' do - visit root_dashboard_path - open_top_nav - - link_text = Feature.enabled?(:combined_menu, default_enabled: :yaml) ? 'Admin' : 'Admin Area' - expect(page).to have_link(text: link_text, href: admin_root_path, visible: true) - expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true) - end - - it 'relocates admin dashboard links to dropdown list on smaller screen', :js do - skip('pending responsive development under :combined_menu feature flag') if Feature.enabled?(:combined_menu, default_enabled: :yaml) - - resize_screen_xs - visit root_dashboard_path - - expect(page).not_to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true) - - find('.header-more').click - - page.within '.navbar' do - expect(page).to have_link(text: 'Admin Area', href: admin_root_path, visible: true) - expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true) - end - end + context 'on a read-only instance' do + before do + allow(Gitlab::Database).to receive(:read_only?).and_return(true) end - context 'on a read-only instance' do - before do - allow(Gitlab::Database).to receive(:read_only?).and_return(true) - end - - it 'can leave admin mode', :js do - gitlab_disable_admin_mode + it 'can leave admin mode' do + gitlab_disable_admin_mode - open_top_nav + open_top_nav - within_top_nav do - expect(page).to have_link(href: new_admin_session_path) - end + within_top_nav do + expect(page).to have_link(href: new_admin_session_path) end end end end - - context 'application setting :admin_mode is disabled' do - before do - stub_application_setting(admin_mode: false) - sign_in(admin) - end - - it 'shows no admin mode buttons in navbar' do - visit admin_root_path - open_top_nav - - expect(page).not_to have_link(href: new_admin_session_path) - expect(page).not_to have_link(href: destroy_admin_session_path) - end - end end - context 'with combined_menu feature flag on', :js do - let(:needs_rewrite_for_combined_menu_flag_on) { true } - + context 'application setting :admin_mode is disabled' do before do - stub_feature_flags(combined_menu: true) + stub_application_setting(admin_mode: false) + sign_in(admin) end - it_behaves_like 'combined_menu: feature flag examples' - end - - context 'with combined_menu feature flag off' do - let(:needs_rewrite_for_combined_menu_flag_on) { false } + it 'shows no admin mode buttons in navbar' do + visit admin_root_path + open_top_nav - before do - stub_feature_flags(combined_menu: false) + expect(page).not_to have_link(href: new_admin_session_path) + expect(page).not_to have_link(href: destroy_admin_session_path) end - - it_behaves_like 'combined_menu: feature flag examples' end end diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 9efb31ef4c1..4a0f7ccbb0a 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -190,7 +190,7 @@ RSpec.describe 'Admin updates settings' do accept_terms(admin) page.within('.as-terms') do - check 'Require all users to accept Terms of Service and Privacy Policy when they access GitLab.' + check 'All users must accept the Terms of Service and Privacy Policy to access GitLab' fill_in 'Terms of Service Agreement', with: 'Be nice!' click_button 'Save changes' end @@ -490,7 +490,7 @@ RSpec.describe 'Admin updates settings' do it 'change Prometheus settings' do page.within('.as-prometheus') do - check 'Enable Prometheus Metrics' + check 'Enable health and performance metrics endpoint' click_button 'Save changes' end @@ -502,23 +502,23 @@ RSpec.describe 'Admin updates settings' do group = create(:group) page.within('.as-performance-bar') do - check 'Enable access to the Performance Bar' - fill_in 'Allowed group', with: group.path + check 'Allow non-administrators to access to the performance bar' + fill_in 'Allow access to members of the following group', with: group.path click_on 'Save changes' end expect(page).to have_content "Application settings saved successfully" - expect(find_field('Enable access to the Performance Bar')).to be_checked - expect(find_field('Allowed group').value).to eq group.path + expect(find_field('Allow non-administrators to access to the performance bar')).to be_checked + expect(find_field('Allow access to members of the following group').value).to eq group.path page.within('.as-performance-bar') do - uncheck 'Enable access to the Performance Bar' + uncheck 'Allow non-administrators to access to the performance bar' click_on 'Save changes' end expect(page).to have_content 'Application settings saved successfully' - expect(find_field('Enable access to the Performance Bar')).not_to be_checked - expect(find_field('Allowed group').value).to be_nil + expect(find_field('Allow non-administrators to access to the performance bar')).not_to be_checked + expect(find_field('Allow access to members of the following group').value).to be_nil end it 'loads usage ping payload on click', :js do @@ -585,7 +585,7 @@ RSpec.describe 'Admin updates settings' do page.within('.as-help-page') do fill_in 'Additional text to show on the Help page', with: 'Example text' - check 'Hide marketing-related entries from the Help page.' + check 'Hide marketing-related entries from the Help page' fill_in 'Support page URL', with: new_support_url fill_in 'Documentation pages URL', with: new_documentation_url click_button 'Save changes' @@ -634,7 +634,7 @@ RSpec.describe 'Admin updates settings' do it "change Pages Let's Encrypt settings" do visit preferences_admin_application_settings_path page.within('.as-pages') do - fill_in 'Email', with: 'my@test.example.com' + fill_in "Let's Encrypt email", with: 'my@test.example.com' check "I have read and agree to the Let's Encrypt Terms of Service" click_button 'Save changes' end diff --git a/spec/features/admin/dashboard_spec.rb b/spec/features/admin/dashboard_spec.rb index 618fae3e46b..112dc9e01d8 100644 --- a/spec/features/admin/dashboard_spec.rb +++ b/spec/features/admin/dashboard_spec.rb @@ -19,8 +19,8 @@ RSpec.describe 'admin visits dashboard' do # Make sure the fork_networks & fork_networks reltuples have been updated # to get a correct count on postgresql - ActiveRecord::Base.connection.execute('ANALYZE fork_networks') - ActiveRecord::Base.connection.execute('ANALYZE fork_network_members') + ForkNetwork.connection.execute('ANALYZE fork_networks') + ForkNetwork.connection.execute('ANALYZE fork_network_members') visit admin_root_path diff --git a/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb b/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb index 6f091d37995..22a27b33671 100644 --- a/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb +++ b/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb @@ -11,6 +11,12 @@ RSpec.describe 'User activates the instance-level Mattermost Slash Command integ end let(:edit_path) { edit_admin_application_settings_integration_path(:mattermost_slash_commands) } + let(:overrides_path) { overrides_admin_application_settings_integration_path(:mattermost_slash_commands) } include_examples 'user activates the Mattermost Slash Command integration' + + it 'displays navigation tabs' do + expect(page).to have_link('Settings', href: edit_path) + expect(page).to have_link('Projects using custom settings', href: overrides_path) + 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 deleted file mode 100644 index d367867ebb5..00000000000 --- a/spec/features/admin/services/admin_visits_service_templates_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Admin visits service templates' do - let(:admin) { create(:user, :admin) } - let(:slack_integration) { Integration.for_template.find { |s| s.type == 'SlackService' } } - - before do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - end - - 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 an active service template' do - before do - create(:integrations_slack, :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_integration.id)) - expect(page).not_to have_link('Slack', href: edit_admin_application_settings_integration_path(slack_integration)) - end - end - - context 'with instance-level integration' do - before do - create(:integrations_slack, 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_integration.id)) - expect(page).to have_link('Slack', href: edit_admin_application_settings_integration_path(slack_integration)) - end - end - end -end diff --git a/spec/features/admin/users/user_spec.rb b/spec/features/admin/users/user_spec.rb index e6eb76b13eb..624bfde7359 100644 --- a/spec/features/admin/users/user_spec.rb +++ b/spec/features/admin/users/user_spec.rb @@ -90,6 +90,39 @@ RSpec.describe 'Admin::Users::User' do end end + context 'when user is the sole owner of a group' do + let_it_be(:group) { create(:group) } + let_it_be(:user_sole_owner_of_group) { create(:user) } + + before do + group.add_owner(user_sole_owner_of_group) + end + + it 'shows `Delete user and contributions` action but not `Delete user` action', :js do + visit admin_user_path(user_sole_owner_of_group) + + click_user_dropdown_toggle(user_sole_owner_of_group.id) + + expect(page).to have_button('Delete user and contributions') + expect(page).not_to have_button('Delete user', exact: true) + end + + it 'allows user to be deleted by using the `Delete user and contributions` action', :js do + visit admin_user_path(user_sole_owner_of_group) + + click_action_in_user_dropdown(user_sole_owner_of_group.id, 'Delete user and contributions') + + page.within('[role="dialog"]') do + fill_in('username', with: user_sole_owner_of_group.name) + click_button('Delete user and contributions') + end + + wait_for_requests + + expect(page).to have_content('The user is being deleted.') + end + end + describe 'Impersonation' do let_it_be(:another_user) { create(:user) } @@ -151,7 +184,7 @@ RSpec.describe 'Admin::Users::User' do it 'logs in as the user when impersonate is clicked' do subject - find('[data-qa-selector="user_menu"]').click + find('[data-qa-selector="user_menu"]').click # rubocop:disable QA/SelectorUsage expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eql(another_user.username) end @@ -187,7 +220,7 @@ RSpec.describe 'Admin::Users::User' do it 'logs out of impersonated user back to original user' do subject - find('[data-qa-selector="user_menu"]').click + find('[data-qa-selector="user_menu"]').click # rubocop:disable QA/SelectorUsage expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eq(current_user.username) end diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index 4b52bb953ed..9a5b5bbfc34 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe 'Project issue boards', :js do include DragTo include MobileHelpers + include BoardHelpers let_it_be(:group) { create(:group, :nested) } let_it_be(:project) { create(:project, :public, namespace: group) } @@ -12,584 +13,508 @@ RSpec.describe 'Project issue boards', :js do let_it_be(:user) { create(:user) } let_it_be(:user2) { create(:user) } - before do - project.add_maintainer(user) - project.add_maintainer(user2) - - sign_in(user) - - set_cookie('sidebar_collapsed', 'true') - end - - context 'no lists' do + context 'signed in user' do before do - visit_project_board_path_without_query_limit(project, board) - end - - it 'creates default lists' do - lists = %w[Open Closed] - - wait_for_requests + project.add_maintainer(user) + project.add_maintainer(user2) - expect(page).to have_selector('.board', count: 2) + sign_in(user) - page.all('.board').each_with_index do |list, i| - expect(list.find('.board-title')).to have_content(lists[i]) - end + set_cookie('sidebar_collapsed', 'true') end - end - context 'with lists' do - let_it_be(:milestone) { create(:milestone, project: project) } - - let_it_be(:planning) { create(:label, project: project, name: 'Planning', description: 'Test') } - let_it_be(:development) { create(:label, project: project, name: 'Development') } - let_it_be(:testing) { create(:label, project: project, name: 'Testing') } - let_it_be(:bug) { create(:label, project: project, name: 'Bug') } - let_it_be(:backlog) { create(:label, project: project, name: 'Backlog') } - let_it_be(:closed) { create(:label, project: project, name: 'Closed') } - let_it_be(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') } - 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) } - let_it_be(:issue2) { create(:labeled_issue, project: project, title: 'bbb', description: '222', author: user2, labels: [planning], relative_position: 7) } - let_it_be(:issue3) { create(:labeled_issue, project: project, title: 'ccc', description: '333', labels: [planning], relative_position: 6) } - let_it_be(:issue4) { create(:labeled_issue, project: project, title: 'ddd', description: '444', labels: [planning], relative_position: 5) } - let_it_be(:issue5) { create(:labeled_issue, project: project, title: 'eee', description: '555', labels: [planning], milestone: milestone, relative_position: 4) } - let_it_be(:issue6) { create(:labeled_issue, project: project, title: 'fff', description: '666', labels: [planning, development], relative_position: 3) } - let_it_be(:issue7) { create(:labeled_issue, project: project, title: 'ggg', description: '777', labels: [development], relative_position: 2) } - let_it_be(:issue8) { create(:closed_issue, project: project, title: 'hhh', description: '888') } - let_it_be(:issue9) { create(:labeled_issue, project: project, title: 'iii', description: '999', labels: [planning, testing, bug, accepting], relative_position: 1) } - let_it_be(:issue10) { create(:labeled_issue, project: project, title: 'issue +', description: 'A+ great issue', labels: [a_plus]) } - - before do - stub_feature_flags(board_new_list: false) + context 'no lists' do + before do + visit_project_board_path_without_query_limit(project, board) + end - visit_project_board_path_without_query_limit(project, board) + it 'creates default lists' do + lists = %w[Open Closed] - wait_for_requests + wait_for_requests - expect(page).to have_selector('.board', count: 4) - expect(find('.board:nth-child(2)')).to have_selector('.board-card') - expect(find('.board:nth-child(3)')).to have_selector('.board-card') - expect(find('.board:nth-child(4)')).to have_selector('.board-card') - end + expect(page).to have_selector('.board', count: 2) - it 'shows description tooltip on list title', :quarantine do - page.within('.board:nth-child(2)') do - expect(find('.board-title span.has-tooltip')[:title]).to eq('Test') + page.all('.board').each_with_index do |list, i| + expect(list.find('.board-title')).to have_content(lists[i]) + end end end - it 'shows issues in lists' do - wait_for_board_cards(2, 8) - wait_for_board_cards(3, 2) - end + context 'with lists' do + let_it_be(:milestone) { create(:milestone, project: project) } + + let_it_be(:planning) { create(:label, project: project, name: 'Planning', description: 'Test') } + let_it_be(:development) { create(:label, project: project, name: 'Development') } + let_it_be(:testing) { create(:label, project: project, name: 'Testing') } + let_it_be(:bug) { create(:label, project: project, name: 'Bug') } + let_it_be(:backlog) { create(:label, project: project, name: 'Backlog') } + let_it_be(:closed) { create(:label, project: project, name: 'Closed') } + let_it_be(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') } + 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) } + let_it_be(:issue2) { create(:labeled_issue, project: project, title: 'bbb', description: '222', author: user2, labels: [planning], relative_position: 7) } + let_it_be(:issue3) { create(:labeled_issue, project: project, title: 'ccc', description: '333', labels: [planning], relative_position: 6) } + let_it_be(:issue4) { create(:labeled_issue, project: project, title: 'ddd', description: '444', labels: [planning], relative_position: 5) } + let_it_be(:issue5) { create(:labeled_issue, project: project, title: 'eee', description: '555', labels: [planning], milestone: milestone, relative_position: 4) } + let_it_be(:issue6) { create(:labeled_issue, project: project, title: 'fff', description: '666', labels: [planning, development], relative_position: 3) } + let_it_be(:issue7) { create(:labeled_issue, project: project, title: 'ggg', description: '777', labels: [development], relative_position: 2) } + let_it_be(:issue8) { create(:closed_issue, project: project, title: 'hhh', description: '888') } + let_it_be(:issue9) { create(:labeled_issue, project: project, title: 'iii', description: '999', labels: [planning, testing, bug, accepting], relative_position: 1) } + let_it_be(:issue10) { create(:labeled_issue, project: project, title: 'issue +', description: 'A+ great issue', labels: [a_plus]) } - it 'shows confidential issues with icon' do - page.within(find('.board:nth-child(2)')) do - expect(page).to have_selector('.confidential-icon', count: 1) + before do + visit_project_board_path_without_query_limit(project, board) end - end - - it 'search closed list' do - find('.filtered-search').set(issue8.title) - find('.filtered-search').native.send_keys(:enter) - wait_for_requests + it 'shows description tooltip on list title', :quarantine do + page.within('.board:nth-child(2)') do + expect(find('.board-title span.has-tooltip')[:title]).to eq('Test') + end + end - expect(find('.board:nth-child(2)')).to have_selector('.board-card', count: 0) - expect(find('.board:nth-child(3)')).to have_selector('.board-card', count: 0) - expect(find('.board:nth-child(4)')).to have_selector('.board-card', count: 1) - end + it 'shows issues in lists' do + wait_for_board_cards(2, 8) + wait_for_board_cards(3, 2) + end - it 'search list' do - find('.filtered-search').set(issue5.title) - find('.filtered-search').native.send_keys(:enter) + it 'shows confidential issues with icon' do + page.within(find('.board:nth-child(2)')) do + expect(page).to have_selector('.confidential-icon', count: 1) + end + end - wait_for_requests + it 'search closed list' do + find('.filtered-search').set(issue8.title) + find('.filtered-search').native.send_keys(:enter) - expect(find('.board:nth-child(2)')).to have_selector('.board-card', count: 1) - expect(find('.board:nth-child(3)')).to have_selector('.board-card', count: 0) - expect(find('.board:nth-child(4)')).to have_selector('.board-card', count: 0) - end + wait_for_requests - context 'search list negation queries' do - before do - visit_project_board_path_without_query_limit(project, board) + expect(find('.board:nth-child(2)')).to have_selector('.board-card', count: 0) + expect(find('.board:nth-child(3)')).to have_selector('.board-card', count: 0) + expect(find('.board:nth-child(4)')).to have_selector('.board-card', count: 1) end - it 'does not have the != option' do - find('.filtered-search').set('label:') + it 'search list' do + find('.filtered-search').set(issue5.title) + find('.filtered-search').native.send_keys(:enter) wait_for_requests - within('#js-dropdown-operator') do - tokens = all(:css, 'li.filter-dropdown-item') - expect(tokens.count).to eq(2) - button = tokens[0].find('button') - expect(button).to have_content('=') - button = tokens[1].find('button') - expect(button).to have_content('!=') - end - end - end - it 'allows user to delete board' do - remove_list + expect(find('.board:nth-child(2)')).to have_selector('.board-card', count: 1) + expect(find('.board:nth-child(3)')).to have_selector('.board-card', count: 0) + expect(find('.board:nth-child(4)')).to have_selector('.board-card', count: 0) + end - wait_for_requests + context 'search list negation queries' do + before do + visit_project_board_path_without_query_limit(project, board) + end - expect(page).to have_selector('.board', count: 3) - end + it 'does not have the != option' do + find('.filtered-search').set('label:') - it 'infinite scrolls list' do - create_list(:labeled_issue, 30, project: project, labels: [planning]) + wait_for_requests + within('#js-dropdown-operator') do + tokens = all(:css, 'li.filter-dropdown-item') + expect(tokens.count).to eq(2) + button = tokens[0].find('button') + expect(button).to have_content('=') + button = tokens[1].find('button') + expect(button).to have_content('!=') + end + end + end - visit_project_board_path_without_query_limit(project, board) + it 'allows user to delete board' do + remove_list - page.within(find('.board:nth-child(2)')) do - expect(page.find('.board-header')).to have_content('38') - expect(page).to have_selector('.board-card', count: 10) - expect(page).to have_content('Showing 10 of 38 issues') + wait_for_requests - find('.board .board-list') + expect(page).to have_selector('.board', count: 3) + end - 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 + it 'infinite scrolls list' do + create_list(:labeled_issue, 30, project: project, labels: [planning]) - expect(page).to have_selector('.board-card', count: 20) - expect(page).to have_content('Showing 20 of 38 issues') + visit_project_board_path_without_query_limit(project, board) - find('.board .board-list') + page.within(find('.board:nth-child(2)')) do + expect(page.find('.board-header')).to have_content('38') + expect(page).to have_selector('.board-card', count: 10) + expect(page).to have_content('Showing 10 of 38 issues') - 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 + find('.board .board-list') - expect(page).to have_selector('.board-card', count: 30) - expect(page).to have_content('Showing 30 of 38 issues') + 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 - find('.board .board-list') + expect(page).to have_selector('.board-card', count: 20) + expect(page).to have_content('Showing 20 of 38 issues') - 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 + find('.board .board-list') - expect(page).to have_selector('.board-card', count: 38) - expect(page).to have_content('Showing all issues') - end - end + 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 - context 'closed' do - it 'shows list of closed issues' do - wait_for_board_cards(4, 1) - wait_for_requests - end + expect(page).to have_selector('.board-card', count: 30) + expect(page).to have_content('Showing 30 of 38 issues') - it 'moves issue to closed' do - drag(list_from_index: 1, list_to_index: 3) + find('.board .board-list') - wait_for_board_cards(2, 7) - wait_for_board_cards(3, 2) - wait_for_board_cards(4, 2) + 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(find('.board:nth-child(2)')).not_to have_content(issue9.title) - expect(find('.board:nth-child(4)')).to have_selector('.board-card', count: 2) - expect(find('.board:nth-child(4)')).to have_content(issue9.title) - expect(find('.board:nth-child(4)')).not_to have_content(planning.title) + expect(page).to have_selector('.board-card', count: 38) + expect(page).to have_content('Showing all issues') + end end - it 'removes all of the same issue to closed' do - drag(list_from_index: 1, list_to_index: 3) - - wait_for_board_cards(2, 7) - wait_for_board_cards(3, 2) - wait_for_board_cards(4, 2) + context 'closed' do + it 'shows list of closed issues' do + wait_for_board_cards(4, 1) + wait_for_requests + end - expect(find('.board:nth-child(2)')).not_to have_content(issue9.title) - expect(find('.board:nth-child(4)')).to have_content(issue9.title) - expect(find('.board:nth-child(4)')).not_to have_content(planning.title) - end - end + it 'moves issue to closed' do + drag(list_from_index: 1, list_to_index: 3) - context 'lists' do - it 'changes position of list' do - drag(list_from_index: 2, list_to_index: 1, selector: '.board-header') + wait_for_board_cards(2, 7) + wait_for_board_cards(3, 2) + wait_for_board_cards(4, 2) - wait_for_board_cards(2, 2) - wait_for_board_cards(3, 8) - wait_for_board_cards(4, 1) + expect(find('.board:nth-child(2)')).not_to have_content(issue9.title) + expect(find('.board:nth-child(4)')).to have_selector('.board-card', count: 2) + expect(find('.board:nth-child(4)')).to have_content(issue9.title) + expect(find('.board:nth-child(4)')).not_to have_content(planning.title) + end - expect(find('.board:nth-child(2)')).to have_content(development.title) - expect(find('.board:nth-child(3)')).to have_content(planning.title) + it 'removes all of the same issue to closed' do + drag(list_from_index: 1, list_to_index: 3) - # Make sure list positions are preserved after a reload - visit_project_board_path_without_query_limit(project, board) + wait_for_board_cards(2, 7) + wait_for_board_cards(3, 2) + wait_for_board_cards(4, 2) - expect(find('.board:nth-child(2)')).to have_content(development.title) - expect(find('.board:nth-child(3)')).to have_content(planning.title) + expect(find('.board:nth-child(2)')).not_to have_content(issue9.title) + expect(find('.board:nth-child(4)')).to have_content(issue9.title) + expect(find('.board:nth-child(4)')).not_to have_content(planning.title) + end end - it 'dragging does not duplicate list' do - selector = '.board:not(.is-ghost) .board-header' - expect(page).to have_selector(selector, text: development.title, count: 1) + context 'lists' do + it 'changes position of list' do + drag(list_from_index: 2, list_to_index: 1, selector: '.board-header') - drag(list_from_index: 2, list_to_index: 1, selector: '.board-header', perform_drop: false) + expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(development.title) + expect(find('.board:nth-child(3) [data-testid="board-list-header"]')).to have_content(planning.title) - expect(page).to have_selector(selector, text: development.title, count: 1) - end + # Make sure list positions are preserved after a reload + visit_project_board_path_without_query_limit(project, board) - it '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) + expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(development.title) + expect(find('.board:nth-child(3) [data-testid="board-list-header"]')).to have_content(planning.title) + end - wait_for_board_cards(2, 7) - wait_for_board_cards(3, 2) - wait_for_board_cards(4, 1) + context 'without backlog and closed lists' do + let_it_be(:board) { create(:board, project: project, hide_backlog_list: true, hide_closed_list: true) } + let_it_be(:list1) { create(:list, board: board, label: planning, position: 0) } + let_it_be(:list2) { create(:list, board: board, label: development, position: 1) } - expect(find('.board:nth-child(3)')).to have_content(issue6.title) - expect(find('.board:nth-child(3)').all('.board-card').last).not_to have_content(development.title) - end + it 'changes position of list' do + visit_project_board_path_without_query_limit(project, board) - it 'issue moves between lists and does not show the "Planning" label since the card is in the "Planning" list label' do - drag(list_from_index: 2, list_to_index: 1) + drag(list_from_index: 0, list_to_index: 1, selector: '.board-header') - wait_for_board_cards(2, 9) - wait_for_board_cards(3, 1) - wait_for_board_cards(4, 1) + expect(find('.board:nth-child(1) [data-testid="board-list-header"]')).to have_content(development.title) + expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(planning.title) - expect(find('.board:nth-child(2)')).to have_content(issue7.title) - expect(find('.board:nth-child(2)').all('.board-card').first).not_to have_content(planning.title) - end + # Make sure list positions are preserved after a reload + visit_project_board_path_without_query_limit(project, board) - it 'issue moves from closed' do - drag(list_from_index: 2, list_to_index: 3) + expect(find('.board:nth-child(1) [data-testid="board-list-header"]')).to have_content(development.title) + expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(planning.title) + end + end - wait_for_board_cards(2, 8) - wait_for_board_cards(3, 1) - wait_for_board_cards(4, 2) + it 'dragging does not duplicate list' do + selector = '.board:not(.is-ghost) .board-header' + expect(page).to have_selector(selector, text: development.title, count: 1) - expect(find('.board:nth-child(4)')).to have_content(issue8.title) - end + drag(list_from_index: 2, list_to_index: 1, selector: '.board-header', perform_drop: false) - context 'issue card' do - it 'shows assignee' do - page.within(find('.board:nth-child(2)')) do - expect(page).to have_selector('.avatar', count: 1) - end + expect(page).to have_selector(selector, text: development.title, count: 1) end - context 'list header' do - let(:total_planning_issues) { "8" } + it '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) - it 'shows issue count on the list' do - page.within(find(".board:nth-child(2)")) do - expect(page.find('[data-testid="board-items-count"]')).to have_text(total_planning_issues) - expect(page).not_to have_selector('.js-max-issue-size') - end - end + wait_for_board_cards(2, 7) + wait_for_board_cards(3, 2) + wait_for_board_cards(4, 1) + + expect(find('.board:nth-child(3)')).to have_content(issue6.title) + expect(find('.board:nth-child(3)').all('.board-card').last).not_to have_content(development.title) end - end - context 'new list' do - it 'shows all labels in new list dropdown' do - click_button 'Add list' + it 'issue moves between lists and does not show the "Planning" label since the card is in the "Planning" list label' do + drag(list_from_index: 2, list_to_index: 1) - wait_for_requests + wait_for_board_cards(2, 9) + wait_for_board_cards(3, 1) + wait_for_board_cards(4, 1) - page.within('.dropdown-menu-issues-board-new') do - expect(page).to have_content(planning.title) - expect(page).to have_content(development.title) - expect(page).to have_content(testing.title) - end + expect(find('.board:nth-child(2)')).to have_content(issue7.title) + expect(find('.board:nth-child(2)').all('.board-card').first).not_to have_content(planning.title) end - it 'creates new list for label' do - click_button 'Add list' - wait_for_requests - - page.within('.dropdown-menu-issues-board-new') do - click_link testing.title - end + it 'issue moves from closed' do + drag(list_from_index: 2, list_to_index: 3) - wait_for_requests + wait_for_board_cards(2, 8) + wait_for_board_cards(3, 1) + wait_for_board_cards(4, 2) - expect(page).to have_selector('.board', count: 5) + expect(find('.board:nth-child(4)')).to have_content(issue8.title) end - it 'creates new list for Backlog label' do - click_button 'Add list' - wait_for_requests - - page.within('.dropdown-menu-issues-board-new') do - click_link backlog.title + context 'issue card' do + it 'shows assignee' do + page.within(find('.board:nth-child(2)')) do + expect(page).to have_selector('.avatar', count: 1) + end end - wait_for_requests + context 'list header' do + let(:total_planning_issues) { "8" } - expect(page).to have_selector('.board', count: 5) + it 'shows issue count on the list' do + page.within(find(".board:nth-child(2)")) do + expect(page.find('[data-testid="board-items-count"]')).to have_text(total_planning_issues) + expect(page).not_to have_selector('.js-max-issue-size') + end + end + end end + end + + context 'filtering' do + it 'filters by author' do + set_filter("author", user2.username) + click_filter_link(user2.username) + submit_filter - it 'creates new list for Closed label' do - click_button 'Add list' wait_for_requests + wait_for_board_cards(2, 1) + wait_for_empty_boards((3..4)) + end - page.within('.dropdown-menu-issues-board-new') do - click_link closed.title - end + it 'filters by assignee' do + set_filter("assignee", user.username) + click_filter_link(user.username) + submit_filter wait_for_requests - expect(page).to have_selector('.board', count: 5) + wait_for_board_cards(2, 1) + wait_for_empty_boards((3..4)) end - it 'keeps dropdown open after adding new list' do - click_button 'Add list' - wait_for_requests - - page.within('.dropdown-menu-issues-board-new') do - click_link closed.title - end + it 'filters by milestone' do + set_filter("milestone", "\"#{milestone.title}") + click_filter_link(milestone.title) + submit_filter wait_for_requests - - expect(page).to have_css('#js-add-list.show') + wait_for_board_cards(2, 1) + wait_for_board_cards(3, 0) + wait_for_board_cards(4, 0) end - it 'creates new list from a new label' do - click_button 'Add list' + it 'filters by label' do + set_filter("label", testing.title) + click_filter_link(testing.title) + submit_filter wait_for_requests + wait_for_board_cards(2, 1) + wait_for_empty_boards((3..4)) + end - click_link 'Create project label' + it 'filters by label with encoded character' do + set_filter("label", a_plus.title) + click_filter_link(a_plus.title) + submit_filter - fill_in('new_label_name', with: 'Testing New Label - with list') + wait_for_board_cards(1, 1) + wait_for_empty_boards((2..4)) + end - first('.suggest-colors a').click + it 'filters by label with space after reload', :quarantine do + set_filter("label", "\"#{accepting.title}") + click_filter_link(accepting.title) + submit_filter - click_button 'Create' + # Test after reload + page.evaluate_script 'window.location.reload()' + wait_for_board_cards(2, 1) + wait_for_empty_boards((3..4)) wait_for_requests - wait_for_requests - expect(page).to have_selector('.board', count: 5) + page.within(find('.board:nth-child(2)')) do + expect(page.find('.board-header')).to have_content('1') + expect(page).to have_selector('.board-card', count: 1) + end + + page.within(find('.board:nth-child(3)')) do + expect(page.find('.board-header')).to have_content('0') + expect(page).to have_selector('.board-card', count: 0) + end end - end - end - context 'filtering' do - it 'filters by author' do - set_filter("author", user2.username) - click_filter_link(user2.username) - submit_filter + it 'removes filtered labels' do + 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_requests - wait_for_board_cards(2, 1) - wait_for_empty_boards((3..4)) - end + wait_for_board_cards(2, 1) - it 'filters by assignee' do - set_filter("assignee", user.username) - click_filter_link(user.username) - submit_filter + find('.clear-search').click + submit_filter + end - wait_for_requests + wait_for_board_cards(2, 8) + end - wait_for_board_cards(2, 1) - wait_for_empty_boards((3..4)) - end + it 'infinite scrolls list with label filter' do + create_list(:labeled_issue, 30, project: project, labels: [planning, testing]) - it 'filters by milestone' do - set_filter("milestone", "\"#{milestone.title}") - click_filter_link(milestone.title) - submit_filter + set_filter("label", testing.title) + click_filter_link(testing.title) + 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 - wait_for_board_cards(2, 1) - wait_for_board_cards(3, 0) - wait_for_board_cards(4, 0) - end + wait_for_requests - it 'filters by label' do - set_filter("label", testing.title) - click_filter_link(testing.title) - submit_filter + page.within(find('.board:nth-child(2)')) do + expect(page.find('.board-header')).to have_content('31') + expect(page).to have_selector('.board-card', count: 10) + expect(page).to have_content('Showing 10 of 31 issues') - wait_for_requests - wait_for_board_cards(2, 1) - wait_for_empty_boards((3..4)) - end + find('.board .board-list') - it 'filters by label with encoded character' do - set_filter("label", a_plus.title) - click_filter_link(a_plus.title) - submit_filter + 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 - wait_for_board_cards(1, 1) - wait_for_empty_boards((2..4)) - end + expect(page).to have_selector('.board-card', count: 20) + expect(page).to have_content('Showing 20 of 31 issues') - it 'filters by label with space after reload', :quarantine do - set_filter("label", "\"#{accepting.title}") - click_filter_link(accepting.title) - submit_filter + find('.board .board-list') - # Test after reload - page.evaluate_script 'window.location.reload()' - wait_for_board_cards(2, 1) - wait_for_empty_boards((3..4)) + 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 - wait_for_requests + expect(page).to have_selector('.board-card', count: 30) + expect(page).to have_content('Showing 30 of 31 issues') - page.within(find('.board:nth-child(2)')) do - expect(page.find('.board-header')).to have_content('1') - expect(page).to have_selector('.board-card', count: 1) - end + find('.board .board-list') + 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 - page.within(find('.board:nth-child(3)')) do - expect(page.find('.board-header')).to have_content('0') - expect(page).to have_selector('.board-card', count: 0) + expect(page).to have_selector('.board-card', count: 31) + expect(page).to have_content('Showing all issues') + end end - end - it 'removes filtered labels' do - inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do + it 'filters by multiple labels', :quarantine do set_filter("label", testing.title) click_filter_link(testing.title) - submit_filter - wait_for_board_cards(2, 1) + set_filter("label", bug.title) + click_filter_link(bug.title) - find('.clear-search').click submit_filter - end - wait_for_board_cards(2, 8) - end - - it 'infinite scrolls list with label filter' do - create_list(:labeled_issue, 30, project: project, labels: [planning, testing]) + wait_for_requests - set_filter("label", testing.title) - click_filter_link(testing.title) - inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do - submit_filter + wait_for_board_cards(2, 1) + wait_for_empty_boards((3..4)) end - wait_for_requests - - page.within(find('.board:nth-child(2)')) do - expect(page.find('.board-header')).to have_content('31') - expect(page).to have_selector('.board-card', count: 10) - expect(page).to have_content('Showing 10 of 31 issues') - - find('.board .board-list') - - 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: 20) - expect(page).to have_content('Showing 20 of 31 issues') - - find('.board .board-list') - - 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") + it 'filters by clicking label button on issue' do + page.within(find('.board:nth-child(2)')) do + expect(page).to have_selector('.board-card', count: 8) + expect(find('.board-card', match: :first)).to have_content(bug.title) + click_link(bug.title) + wait_for_requests end - expect(page).to have_selector('.board-card', count: 30) - expect(page).to have_content('Showing 30 of 31 issues') - - find('.board .board-list') - 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") + page.within('.tokens-container') do + expect(page).to have_content(bug.title) end - expect(page).to have_selector('.board-card', count: 31) - expect(page).to have_content('Showing all issues') - end - end - - it 'filters by multiple labels', :quarantine do - set_filter("label", testing.title) - click_filter_link(testing.title) - - set_filter("label", bug.title) - click_filter_link(bug.title) - - submit_filter - - wait_for_requests - - wait_for_board_cards(2, 1) - wait_for_empty_boards((3..4)) - end - - it 'filters by clicking label button on issue' do - page.within(find('.board:nth-child(2)')) do - expect(page).to have_selector('.board-card', count: 8) - expect(find('.board-card', match: :first)).to have_content(bug.title) - click_link(bug.title) wait_for_requests - end - page.within('.tokens-container') do - expect(page).to have_content(bug.title) + wait_for_board_cards(2, 1) + wait_for_empty_boards((3..4)) end - wait_for_requests + it 'removes label filter by clicking label button on issue' do + page.within(find('.board:nth-child(2)')) do + page.within(find('.board-card', match: :first)) do + click_link(bug.title) + end - wait_for_board_cards(2, 1) - wait_for_empty_boards((3..4)) - end + wait_for_requests - it 'removes label filter by clicking label button on issue' do - page.within(find('.board:nth-child(2)')) do - page.within(find('.board-card', match: :first)) do - click_link(bug.title) + expect(page).to have_selector('.board-card', count: 1) end wait_for_requests - - expect(page).to have_selector('.board-card', count: 1) end - - wait_for_requests end end - end - context 'issue board focus mode' do - before do - visit project_board_path(project, board) - wait_for_requests - end + context 'issue board focus mode' do + before do + visit project_board_path(project, board) + wait_for_requests + end - it 'shows the button' do - expect(page).to have_button('Toggle focus mode') + it 'shows the button' do + expect(page).to have_button('Toggle focus mode') + end end - end - context 'keyboard shortcuts' do - before do - visit_project_board_path_without_query_limit(project, board) - wait_for_requests - end + context 'keyboard shortcuts' do + before do + visit_project_board_path_without_query_limit(project, board) + wait_for_requests + end - it 'allows user to use keyboard shortcuts' do - find('body').native.send_keys('i') - expect(page).to have_content('New Issue') + it 'allows user to use keyboard shortcuts' do + find('body').native.send_keys('i') + expect(page).to have_content('New Issue') + end end end context 'signed out user' do before do - sign_out(:user) visit project_board_path(project, board) wait_for_requests end @@ -599,7 +524,7 @@ RSpec.describe 'Project issue boards', :js do end it 'does not show create new list' do - expect(page).not_to have_button('.js-new-board-list') + expect(page).not_to have_button('Create list') end it 'does not allow dragging' do @@ -612,34 +537,16 @@ RSpec.describe 'Project issue boards', :js do before do project.add_guest(user_guest) - sign_out(:user) sign_in(user_guest) visit project_board_path(project, board) wait_for_requests end it 'does not show create new list' do - expect(page).not_to have_selector('.js-new-board-list') + expect(page).not_to have_button('Create list') end end - 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 - 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) page.within(find(".board:nth-child(#{board_number})")) do expect(page.find('.board-header')).to have_content(expected_cards.to_s) @@ -682,6 +589,8 @@ RSpec.describe 'Project issue boards', :js do 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) + + wait_for_requests end end end diff --git a/spec/features/callouts/service_templates_deprecation_spec.rb b/spec/features/callouts/service_templates_deprecation_spec.rb deleted file mode 100644 index b6403b54e29..00000000000 --- a/spec/features/callouts/service_templates_deprecation_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -# 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_health_dashboard_spec.rb b/spec/features/clusters/cluster_health_dashboard_spec.rb index 20c07f4d6ac..e4a36f654e5 100644 --- a/spec/features/clusters/cluster_health_dashboard_spec.rb +++ b/spec/features/clusters/cluster_health_dashboard_spec.rb @@ -63,21 +63,33 @@ RSpec.describe 'Cluster Health board', :js, :kubeclient, :use_clean_rails_memory context 'connected, prometheus returns data' do before do stub_connected - end - it 'renders charts' do visit cluster_path click_link 'Health' wait_for_requests + end + it 'renders charts' do expect(page).to have_css('.prometheus-graphs') expect(page).to have_css('.prometheus-graph') expect(page).to have_css('.prometheus-graph-title') expect(page).to have_css('[_echarts_instance_]') + expect(page).to have_css('.prometheus-graph', count: 2) expect(page).to have_content('Avg') end + + it 'focuses the single panel on toggle', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338341' do + click_button('More actions') + click_button('Expand panel') + + expect(page).to have_css('.prometheus-graph', count: 1) + + click_button('Collapse panel') + + expect(page).to have_css('.prometheus-graph', count: 2) + end end def stub_empty_response diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb index d0f8767884e..de6cb53fdfa 100644 --- a/spec/features/cycle_analytics_spec.rb +++ b/spec/features/cycle_analytics_spec.rb @@ -6,6 +6,8 @@ RSpec.describe 'Value Stream Analytics', :js do let_it_be(:user) { create(:user) } let_it_be(:guest) { create(:user) } let_it_be(:project) { create(:project, :repository) } + let_it_be(:stage_table_selector) { '[data-testid="vsa-stage-table"]' } + let_it_be(:metrics_selector) { "[data-testid='vsa-time-metrics']" } let(:issue) { create(:issue, project: project, created_at: 2.days.ago) } let(:milestone) { create(:milestone, project: project) } @@ -25,11 +27,13 @@ RSpec.describe 'Value Stream Analytics', :js do wait_for_requests end - it 'shows pipeline summary' do - expect(new_issues_counter).to have_content('-') - expect(commits_counter).to have_content('-') - expect(deploys_counter).to have_content('-') - expect(deployment_frequency_counter).to have_content('-') + it 'displays metrics' do + aggregate_failures 'with relevant values' do + expect(new_issues_counter).to have_content('-') + expect(commits_counter).to have_content('-') + expect(deploys_counter).to have_content('-') + expect(deployment_frequency_counter).to have_content('-') + end end it 'shows active stage with empty message' do @@ -45,9 +49,9 @@ RSpec.describe 'Value Stream Analytics', :js do @build = create_cycle(user, project, issue, mr, milestone, pipeline) deploy_master(user, project) - issue.metrics.update!(first_mentioned_in_commit_at: issue.metrics.first_associated_with_milestone_at + 1.day) + issue.metrics.update!(first_mentioned_in_commit_at: issue.metrics.first_associated_with_milestone_at + 1.hour) merge_request = issue.merge_requests_closing_issues.first.merge_request - merge_request.update!(created_at: issue.metrics.first_associated_with_milestone_at + 1.day) + merge_request.update!(created_at: issue.metrics.first_associated_with_milestone_at + 1.hour) merge_request.metrics.update!( latest_build_started_at: 4.hours.ago, latest_build_finished_at: 3.hours.ago, @@ -59,11 +63,15 @@ RSpec.describe 'Value Stream Analytics', :js do visit project_cycle_analytics_path(project) end - it 'shows pipeline summary' do - expect(new_issues_counter).to have_content('1') - expect(commits_counter).to have_content('2') - expect(deploys_counter).to have_content('1') - expect(deployment_frequency_counter).to have_content('0') + it 'displays metrics' do + metrics_tiles = page.find(metrics_selector) + + aggregate_failures 'with relevant values' do + expect(metrics_tiles).to have_content('Commit') + expect(metrics_tiles).to have_content('Deploy') + expect(metrics_tiles).to have_content('Deployment Frequency') + expect(metrics_tiles).to have_content('New Issue') + end end it 'shows data on each stage', :sidekiq_might_not_need_inline do @@ -76,13 +84,13 @@ RSpec.describe 'Value Stream Analytics', :js do expect_merge_request_to_be_present click_stage('Test') - expect_build_to_be_present + expect_merge_request_to_be_present click_stage('Review') expect_merge_request_to_be_present click_stage('Staging') - expect_build_to_be_present + expect_merge_request_to_be_present end context "when I change the time period observed" do @@ -95,7 +103,7 @@ RSpec.describe 'Value Stream Analytics', :js do end it 'shows only relevant data' do - expect(new_issues_counter).to have_content('1') + expect(new_issue_counter).to have_content('1') end end end @@ -115,60 +123,55 @@ RSpec.describe 'Value Stream Analytics', :js do end it 'does not show the commit stats' do - expect(page).to have_no_selector(:xpath, commits_counter_selector) + expect(page.find(metrics_selector)).not_to have_selector("#commits") end it 'needs permissions to see restricted stages' do - expect(find('.stage-events')).to have_content(issue.title) + expect(find(stage_table_selector)).to have_content(issue.title) click_stage('Code') - expect(find('.stage-events')).to have_content('You need permission.') + expect(find(stage_table_selector)).to have_content('You need permission.') click_stage('Review') - expect(find('.stage-events')).to have_content('You need permission.') + expect(find(stage_table_selector)).to have_content('You need permission.') end end - def new_issues_counter - find(:xpath, "//p[contains(text(),'New Issue')]/preceding-sibling::h3") + def find_metric_tile(sel) + page.find("#{metrics_selector} #{sel}") end - def commits_counter_selector - "//p[contains(text(),'Commits')]/preceding-sibling::h3" + # When now use proper pluralization for the metric names, which affects the id + def new_issue_counter + find_metric_tile("#new-issue") end - def commits_counter - find(:xpath, commits_counter_selector) + def new_issues_counter + find_metric_tile("#new-issues") end - def deploys_counter - find(:xpath, "//p[contains(text(),'Deploy')]/preceding-sibling::h3", match: :first) + def commits_counter + find_metric_tile("#commits") end - def deployment_frequency_counter_selector - "//p[contains(text(),'Deployment Frequency')]/preceding-sibling::h3" + def deploys_counter + find_metric_tile("#deploys") end def deployment_frequency_counter - find(:xpath, deployment_frequency_counter_selector) + find_metric_tile("#deployment-frequency") end def expect_issue_to_be_present - expect(find('.stage-events')).to have_content(issue.title) - expect(find('.stage-events')).to have_content(issue.author.name) - expect(find('.stage-events')).to have_content("##{issue.iid}") - end - - def expect_build_to_be_present - expect(find('.stage-events')).to have_content(@build.ref) - expect(find('.stage-events')).to have_content(@build.short_sha) - expect(find('.stage-events')).to have_content("##{@build.id}") + expect(find(stage_table_selector)).to have_content(issue.title) + expect(find(stage_table_selector)).to have_content(issue.author.name) + expect(find(stage_table_selector)).to have_content("##{issue.iid}") end def expect_merge_request_to_be_present - expect(find('.stage-events')).to have_content(mr.title) - expect(find('.stage-events')).to have_content(mr.author.name) - expect(find('.stage-events')).to have_content("!#{mr.iid}") + expect(find(stage_table_selector)).to have_content(mr.title) + expect(find(stage_table_selector)).to have_content(mr.author.name) + expect(find(stage_table_selector)).to have_content("!#{mr.iid}") end def click_stage(stage_name) diff --git a/spec/features/dashboard/active_tab_spec.rb b/spec/features/dashboard/active_tab_spec.rb deleted file mode 100644 index aa767d75c00..00000000000 --- a/spec/features/dashboard/active_tab_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -# TODO: This entire spec file can be deleted once the combined_menu feature is fully rolled -# out and the flag is removed, because it will then be irrelevant (there will be no more tabs). -# Feature flag removal issue: https://gitlab.com/gitlab-org/gitlab/-/issues/324086 -RSpec.describe 'Dashboard Active Tab', :js do - shared_examples 'combined_menu: feature flag examples' do - before do - sign_in(create(:user)) - end - - shared_examples 'page has active tab' do |title| - it "#{title} tab" do - subject - - expect(page).to have_selector('.navbar-sub-nav li.active', count: 1) - expect(find('.navbar-sub-nav li.active')).to have_content(title) - end - end - - context 'on dashboard projects' do - it_behaves_like 'page has active tab', 'Projects' do - subject { visit dashboard_projects_path } - end - end - - context 'on dashboard groups' do - it_behaves_like 'page has active tab', 'Groups' do - subject { visit dashboard_groups_path } - end - end - end - - context 'with combined_menu feature flag off' do - before do - stub_feature_flags(combined_menu: false) - end - - it_behaves_like 'combined_menu: feature flag examples' - end -end diff --git a/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb b/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb index 3dd993b4bb5..6861fac3cc2 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 @@ -2,66 +2,44 @@ require 'spec_helper' -RSpec.describe 'The group dashboard' do +RSpec.describe 'The group dashboard', :js do include ExternalAuthorizationServiceHelpers include Spec::Support::Helpers::Features::TopNavSpecHelpers let(:user) { create(:user) } - shared_examples 'combined_menu: feature flag examples' do - before do - sign_in user - end - - describe 'The top navigation' do - it 'has all the expected links' do - visit dashboard_groups_path - - open_top_nav - - within_top_nav do - expect(page).to have_button('Projects') - expect(page).to have_button('Groups') - expect(page).to have_link('Activity') - expect(page).to have_link('Milestones') - expect(page).to have_link('Snippets') - end - end + before do + sign_in user + end - it 'hides some links when an external authorization service is enabled' do - enable_external_authorization_service_check - visit dashboard_groups_path + describe 'The top navigation' do + it 'has all the expected links' do + visit dashboard_groups_path - open_top_nav + open_top_nav - within_top_nav do - expect(page).to have_button('Projects') - expect(page).to have_button('Groups') - expect(page).not_to have_link('Activity') - expect(page).not_to have_link('Milestones') - expect(page).to have_link('Snippets') - end + within_top_nav do + expect(page).to have_button('Projects') + expect(page).to have_button('Groups') + expect(page).to have_link('Activity') + expect(page).to have_link('Milestones') + expect(page).to have_link('Snippets') end end - end - context 'with combined_menu feature flag on', :js do - let(:needs_rewrite_for_combined_menu_flag_on) { true } + it 'hides some links when an external authorization service is enabled' do + enable_external_authorization_service_check + visit dashboard_groups_path - before do - stub_feature_flags(combined_menu: true) - end - - it_behaves_like 'combined_menu: feature flag examples' - end + open_top_nav - context 'with combined_menu feature flag off' do - let(:needs_rewrite_for_combined_menu_flag_on) { false } - - before do - stub_feature_flags(combined_menu: false) + within_top_nav do + expect(page).to have_button('Projects') + expect(page).to have_button('Groups') + expect(page).not_to have_link('Activity') + expect(page).not_to have_link('Milestones') + expect(page).to have_link('Snippets') + end end - - it_behaves_like 'combined_menu: feature flag examples' end end diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index 1f0981de7e1..27419479479 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -194,6 +194,29 @@ RSpec.describe 'Dashboard Projects' do end end + describe 'with topics' do + context 'when project has topics' do + before do + project.update_attribute(:topic_list, 'topic1') + end + + it 'shows project topics if exist' do + visit dashboard_projects_path + + expect(page).to have_selector('[data-testid="project_topic_list"]') + expect(page).to have_link('topic1', href: explore_projects_path(topic: 'topic1')) + end + end + + context 'when project does not have topics' do + it 'does not show project topics' do + visit dashboard_projects_path + + expect(page).not_to have_selector('[data-testid="project_topic_list"]') + end + end + end + context 'last push widget', :use_clean_rails_memory_store_caching do before do event = create(:push_event, project: project, author: user) diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb index 7439bfd334b..3f3ab4218f2 100644 --- a/spec/features/dashboard/shortcuts_spec.rb +++ b/spec/features/dashboard/shortcuts_spec.rb @@ -3,89 +3,71 @@ require 'spec_helper' RSpec.describe 'Dashboard shortcuts', :js do - shared_examples 'combined_menu: feature flag examples' do - context 'logged in' do - let(:user) { create(:user) } - let(:project) { create(:project) } + context 'logged in' do + let(:user) { create(:user) } + let(:project) { create(:project) } - before do - project.add_developer(user) - sign_in(user) - visit root_dashboard_path - end + before do + project.add_developer(user) + sign_in(user) + visit root_dashboard_path + end - it 'navigate to tabs' do - find('body').send_keys([:shift, 'I']) + it 'navigate to tabs' do + find('body').send_keys([:shift, 'I']) - check_page_title('Issues') + check_page_title('Issues') - find('body').send_keys([:shift, 'M']) + find('body').send_keys([:shift, 'M']) - check_page_title('Merge requests') + check_page_title('Merge requests') - find('body').send_keys([:shift, 'T']) + find('body').send_keys([:shift, 'T']) - check_page_title('To-Do List') + check_page_title('To-Do List') - find('body').send_keys([:shift, 'G']) + find('body').send_keys([:shift, 'G']) - check_page_title('Groups') + check_page_title('Groups') - find('body').send_keys([:shift, 'P']) + find('body').send_keys([:shift, 'P']) - check_page_title('Projects') + check_page_title('Projects') - find('body').send_keys([:shift, 'A']) + find('body').send_keys([:shift, 'A']) - check_page_title('Activity') + check_page_title('Activity') - find('body').send_keys([:shift, 'L']) + find('body').send_keys([:shift, 'L']) - check_page_title('Milestones') - end + check_page_title('Milestones') end + end - context 'logged out' do - before do - visit explore_root_path - end - - it 'navigate to tabs' do - find('body').send_keys([:shift, 'G']) + context 'logged out' do + before do + visit explore_root_path + end - find('.nothing-here-block') - expect(page).to have_content('No public groups') + it 'navigate to tabs' do + find('body').send_keys([:shift, 'G']) - find('body').send_keys([:shift, 'S']) + find('.nothing-here-block') + expect(page).to have_content('No public groups') - find('.nothing-here-block') - expect(page).to have_content('No snippets found') + find('body').send_keys([:shift, 'S']) - find('body').send_keys([:shift, 'P']) + find('.nothing-here-block') + expect(page).to have_content('No snippets found') - find('.nothing-here-block') - expect(page).to have_content('Explore public groups to find projects to contribute to.') - end - end + find('body').send_keys([:shift, 'P']) - def check_page_title(title) - expect(find('.page-title')).to have_content(title) + find('.nothing-here-block') + expect(page).to have_content('Explore public groups to find projects to contribute to.') end end - context 'with combined_menu feature flag on' do - before do - stub_feature_flags(combined_menu: true) - end - - it_behaves_like 'combined_menu: feature flag examples' - end - - context 'with combined_menu feature flag off' do - before do - stub_feature_flags(combined_menu: false) - end - - it_behaves_like 'combined_menu: feature flag examples' + def check_page_title(title) + expect(find('.page-title')).to have_content(title) 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 5ea42ce39e3..6bc3b745851 100644 --- a/spec/features/frequently_visited_projects_and_groups_spec.rb +++ b/spec/features/frequently_visited_projects_and_groups_spec.rb @@ -7,67 +7,45 @@ RSpec.describe 'Frequently visited items', :js do let_it_be(:user) { create(:user) } - shared_examples 'combined_menu: feature flag examples' do - before do - sign_in(user) - end - - context 'for projects' do - let_it_be(:project) { create(:project, :public) } + before do + sign_in(user) + end - it 'increments localStorage counter when visiting the project' do - visit project_path(project) - open_top_nav_projects + context 'for projects' do + let_it_be(:project) { create(:project, :public) } - frequent_projects = nil + it 'increments localStorage counter when visiting the project' do + visit project_path(project) + open_top_nav_projects - wait_for('localStorage frequent-projects') do - frequent_projects = page.evaluate_script("localStorage['#{user.username}/frequent-projects']") + frequent_projects = nil - frequent_projects.present? - end + wait_for('localStorage frequent-projects') do + frequent_projects = page.evaluate_script("localStorage['#{user.username}/frequent-projects']") - expect(Gitlab::Json.parse(frequent_projects)).to contain_exactly(a_hash_including('id' => project.id, 'frequency' => 1)) + frequent_projects.present? end - end - - context 'for groups' do - let_it_be(:group) { create(:group, :public) } - - it 'increments localStorage counter when visiting the group' do - visit group_path(group) - open_top_nav_groups - - frequent_groups = nil - - wait_for('localStorage frequent-groups') do - frequent_groups = page.evaluate_script("localStorage['#{user.username}/frequent-groups']") - frequent_groups.present? - end - - expect(Gitlab::Json.parse(frequent_groups)).to contain_exactly(a_hash_including('id' => group.id, 'frequency' => 1)) - end + expect(Gitlab::Json.parse(frequent_projects)).to contain_exactly(a_hash_including('id' => project.id, 'frequency' => 1)) end end - context 'with combined_menu feature flag on' do - let(:needs_rewrite_for_combined_menu_flag_on) { true } + context 'for groups' do + let_it_be(:group) { create(:group, :public) } - before do - stub_feature_flags(combined_menu: true) - end + it 'increments localStorage counter when visiting the group' do + visit group_path(group) + open_top_nav_groups - it_behaves_like 'combined_menu: feature flag examples' - end + frequent_groups = nil - context 'with combined_menu feature flag off' do - let(:needs_rewrite_for_combined_menu_flag_on) { false } + wait_for('localStorage frequent-groups') do + frequent_groups = page.evaluate_script("localStorage['#{user.username}/frequent-groups']") - before do - stub_feature_flags(combined_menu: false) - end + frequent_groups.present? + end - it_behaves_like 'combined_menu: feature flag examples' + expect(Gitlab::Json.parse(frequent_groups)).to contain_exactly(a_hash_including('id' => group.id, 'frequency' => 1)) + end end end diff --git a/spec/features/groups/board_sidebar_spec.rb b/spec/features/groups/board_sidebar_spec.rb index 690d661ba2f..e2dd2fecab7 100644 --- a/spec/features/groups/board_sidebar_spec.rb +++ b/spec/features/groups/board_sidebar_spec.rb @@ -19,8 +19,6 @@ RSpec.describe 'Group Issue Boards', :js do let(:card) { find('.board:nth-child(1)').first('.board-card') } before do - # stubbing until sidebar work is done: https://gitlab.com/gitlab-org/gitlab/-/issues/230711 - stub_feature_flags(graphql_board_lists: false) sign_in(user) visit group_board_path(group, board) @@ -32,6 +30,32 @@ RSpec.describe 'Group Issue Boards', :js do click_card(card) page.within('.labels') do + click_button 'Edit' + + wait_for_requests + + page.within('[data-testid="dropdown-content"]') do + expect(page).to have_content(project_1_label.title) + expect(page).to have_content(group_label.title) + expect(page).not_to have_content(project_2_label.title) + end + end + end + end + + context 'when graphql_board_lists FF disabled' do + before do + stub_feature_flags(graphql_board_lists: false) + sign_in(user) + + visit group_board_path(group, board) + wait_for_requests + end + + it 'only shows valid labels for the issue project and group' do + click_card(card) + + page.within('.labels') do click_link 'Edit' wait_for_requests diff --git a/spec/features/groups/board_spec.rb b/spec/features/groups/board_spec.rb index b4c60ff4fa3..afe36dabcb5 100644 --- a/spec/features/groups/board_spec.rb +++ b/spec/features/groups/board_spec.rb @@ -3,16 +3,21 @@ require 'spec_helper' RSpec.describe 'Group Boards' do - let(:group) { create(:group) } - let!(:project) { create(:project_empty_repo, group: group) } - let(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user } + include DragTo + include MobileHelpers + include BoardHelpers - before do - sign_in(user) - end + let_it_be(:group) { create(:group) } + let_it_be(:user) { create(:user) } + + context 'Creates an issue', :js do + let_it_be(:project) { create(:project_empty_repo, group: group) } - context 'Creates a an issue', :js do before do + group.add_maintainer(user) + + sign_in(user) + visit group_boards_path(group) end @@ -39,4 +44,58 @@ RSpec.describe 'Group Boards' do end end end + + context "when user is a Reporter in one of the group's projects", :js do + let_it_be(:board) { create(:board, group: group) } + + let_it_be(:backlog_list) { create(:backlog_list, board: board) } + let_it_be(:group_label1) { create(:group_label, title: "bug", group: group) } + let_it_be(:group_label2) { create(:group_label, title: "dev", group: group) } + let_it_be(:list1) { create(:list, board: board, label: group_label1, position: 0) } + let_it_be(:list2) { create(:list, board: board, label: group_label2, position: 1) } + + let_it_be(:project1) { create(:project_empty_repo, :private, group: group) } + let_it_be(:project2) { create(:project_empty_repo, :private, group: group) } + let_it_be(:issue1) { create(:issue, title: 'issue1', project: project1, labels: [group_label2]) } + let_it_be(:issue2) { create(:issue, title: 'issue2', project: project2) } + + before do + project1.add_guest(user) + project2.add_reporter(user) + + sign_in(user) + + inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do + visit group_boards_path(group) + end + end + + it 'allows user to move issue of project where they are a Reporter' do + expect(find('.board:nth-child(1)')).to have_content(issue2.title) + + drag(list_from_index: 0, from_index: 0, list_to_index: 1) + + expect(find('.board:nth-child(2)')).to have_content(issue2.title) + expect(issue2.reload.labels).to contain_exactly(group_label1) + end + + it 'does not allow user to move issue of project where they are a Guest' do + expect(find('.board:nth-child(3)')).to have_content(issue1.title) + + drag(list_from_index: 2, from_index: 0, list_to_index: 1) + + expect(find('.board:nth-child(3)')).to have_content(issue1.title) + expect(issue1.reload.labels).to contain_exactly(group_label2) + expect(issue2.reload.labels).to eq([]) + end + + it 'does not allow user to re-position lists' do + drag(list_from_index: 1, list_to_index: 2, selector: '.board-header') + + expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(group_label1.title) + expect(find('.board:nth-child(3) [data-testid="board-list-header"]')).to have_content(group_label2.title) + expect(list1.reload.position).to eq(0) + expect(list2.reload.position).to eq(1) + end + end end diff --git a/spec/features/groups/integrations/user_activates_mattermost_slash_command_spec.rb b/spec/features/groups/integrations/user_activates_mattermost_slash_command_spec.rb index 7703268af39..02aa418cd73 100644 --- a/spec/features/groups/integrations/user_activates_mattermost_slash_command_spec.rb +++ b/spec/features/groups/integrations/user_activates_mattermost_slash_command_spec.rb @@ -13,4 +13,9 @@ RSpec.describe 'User activates the group-level Mattermost Slash Command integrat let(:edit_path) { edit_group_settings_integration_path(group, :mattermost_slash_commands) } include_examples 'user activates the Mattermost Slash Command integration' + + it 'does not display the overrides tab' do + expect(page).not_to have_link('Settings', href: edit_path) + expect(page).not_to have_link('Projects using custom settings', href: overrides_admin_application_settings_integration_path(:mattermost_slash_commands)) + end end diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb index 1d57d0a9103..38e829bafcc 100644 --- a/spec/features/groups/members/manage_members_spec.rb +++ b/spec/features/groups/members/manage_members_spec.rb @@ -84,6 +84,33 @@ RSpec.describe 'Groups > Members > Manage members' do property: 'existing_user', user: user1 ) + expect_no_snowplow_event( + category: 'Members::CreateService', + action: 'area_of_focus' + ) + end + + it 'adds a user to group with area_of_focus', :js, :snowplow, :aggregate_failures do + stub_experiments(member_areas_of_focus: :candidate) + group.add_owner(user1) + + visit group_group_members_path(group) + + invite_member(user2.name, role: 'Reporter', area_of_focus: true) + wait_for_requests + + expect_snowplow_event( + category: 'Members::CreateService', + action: 'area_of_focus', + label: 'Contribute to the codebase', + property: group.members.last.id.to_s + ) + expect_snowplow_event( + category: 'Members::CreateService', + action: 'area_of_focus', + label: 'Collaborate on open issues and merge requests', + property: group.members.last.id.to_s + ) end it 'do not disclose email addresses', :js do @@ -193,9 +220,36 @@ RSpec.describe 'Groups > Members > Manage members' do property: 'net_new_user', user: user1 ) + expect_no_snowplow_event( + category: 'Members::CreateService', + action: 'area_of_focus' + ) end end + it 'invite user to group with area_of_focus', :js, :snowplow, :aggregate_failures do + stub_experiments(member_areas_of_focus: :candidate) + group.add_owner(user1) + + visit group_group_members_path(group) + + invite_member('test@example.com', role: 'Reporter', area_of_focus: true) + wait_for_requests + + expect_snowplow_event( + category: 'Members::InviteService', + action: 'area_of_focus', + label: 'Contribute to the codebase', + property: group.members.last.id.to_s + ) + expect_snowplow_event( + category: 'Members::InviteService', + action: 'area_of_focus', + label: 'Collaborate on open issues and merge requests', + property: group.members.last.id.to_s + ) + end + context 'when user is a guest' do before do group.add_guest(user1) diff --git a/spec/features/groups/packages_spec.rb b/spec/features/groups/packages_spec.rb index 752303fdd78..9a7950266a5 100644 --- a/spec/features/groups/packages_spec.rb +++ b/spec/features/groups/packages_spec.rb @@ -52,6 +52,8 @@ RSpec.describe 'Group Packages' do it_behaves_like 'package details link' end + it_behaves_like 'package details link' + it 'allows you to navigate to the project page' do find('[data-testid="root-link"]', text: project.name).click diff --git a/spec/features/groups/settings/manage_applications_spec.rb b/spec/features/groups/settings/manage_applications_spec.rb new file mode 100644 index 00000000000..5f84f61678d --- /dev/null +++ b/spec/features/groups/settings/manage_applications_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'User manages applications' do + let_it_be(:group) { create(:group) } + let_it_be(:user) { create(:user) } + let_it_be(:new_application_path) { group_settings_applications_path(group) } + + before do + group.add_owner(user) + sign_in(user) + end + + include_examples 'manage applications' +end diff --git a/spec/features/groups/settings/packages_and_registries_spec.rb b/spec/features/groups/settings/packages_and_registries_spec.rb index 551a0bc5375..835555480dd 100644 --- a/spec/features/groups/settings/packages_and_registries_spec.rb +++ b/spec/features/groups/settings/packages_and_registries_spec.rb @@ -113,7 +113,7 @@ RSpec.describe 'Group Packages & Registries settings' do end def find_settings_menu - find('ul[data-testid="group-settings-menu"]') + find('.shortcuts-settings ul') end def visit_settings_page 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 c258dd41b03..abf56232aff 100644 --- a/spec/features/groups/settings/user_searches_in_settings_spec.rb +++ b/spec/features/groups/settings/user_searches_in_settings_spec.rb @@ -40,7 +40,7 @@ RSpec.describe 'User searches group settings', :js do visit group_settings_ci_cd_path(group) end - it_behaves_like 'can search settings', 'Variables', 'Runners' + it_behaves_like 'can search settings', 'Variables', 'Auto DevOps' end context 'in Packages & Registries page' do diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index efde570512f..9c11b84fa8f 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -52,7 +52,7 @@ RSpec.describe 'Group' do click_button 'Create group' expect(current_path).to eq(new_group_path) - expect(page).to have_text('Please choose a group URL with no special characters.') + expect(page).to have_text('Please choose a group URL with no special characters or spaces.') end end diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb index b7e1004aef5..d56bedd4852 100644 --- a/spec/features/invites_spec.rb +++ b/spec/features/invites_spec.rb @@ -141,6 +141,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do let(:invite_email) { new_user.email } let(:group_invite) { create(:group_member, :invited, group: group, invite_email: invite_email, created_by: owner) } let(:send_email_confirmation) { true } + let(:extra_params) { { invite_type: Emails::Members::INITIAL_INVITE } } before do stub_application_setting(send_user_confirmation_email: send_email_confirmation) @@ -148,7 +149,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do context 'when registering using invitation email' do before do - visit invite_path(group_invite.raw_invite_token, invite_type: Members::InviteEmailExperiment::INVITE_TYPE) + visit invite_path(group_invite.raw_invite_token, extra_params) end context 'with admin approval required enabled' do @@ -188,11 +189,28 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do end context 'email confirmation enabled' do - context 'with members/invite_email experiment', :experiment do + context 'with invite email acceptance', :snowplow do it 'tracks the accepted invite' do - expect(experiment('members/invite_email')).to track(:accepted) - .with_context(actor: group_invite) - .on_next_instance + fill_in_sign_up_form(new_user) + + expect_snowplow_event( + category: 'RegistrationsController', + action: 'accepted', + label: 'invite_email', + property: group_invite.id.to_s + ) + end + end + + context 'with invite email acceptance for the invite_email_preview_text experiment', :experiment do + let(:extra_params) do + { invite_type: Emails::Members::INITIAL_INVITE, experiment_name: 'invite_email_preview_text' } + end + + it 'tracks the accepted invite' do + expect(experiment(:invite_email_preview_text)).to track(:accepted) + .with_context(actor: group_invite) + .on_next_instance fill_in_sign_up_form(new_user) end 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 a4c0a84af7d..077c363f78b 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 @@ -33,7 +33,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j context 'resolving the thread' do before do - find('button[data-qa-selector="resolve_discussion_button"]').click + find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage 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 ac3471e8401..3ff8fc5ecca 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 @@ -35,7 +35,7 @@ RSpec.describe 'Resolve an open thread in a merge request by creating an issue', context 'resolving the thread' do before do - find('button[data-qa-selector="resolve_discussion_button"]').click + find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage 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 5ca20028485..4bad67acc87 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -6,13 +6,13 @@ RSpec.describe 'New/edit issue', :js do include ActionView::Helpers::JavaScriptHelper include FormHelper - let!(:project) { create(:project) } - let!(:user) { create(:user)} - let!(:user2) { create(:user)} - let!(:milestone) { create(:milestone, project: project) } - let!(:label) { create(:label, project: project) } - let!(:label2) { create(:label, project: project) } - let!(:issue) { create(:issue, project: project, assignees: [user], milestone: milestone) } + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user)} + let_it_be(:user2) { create(:user)} + let_it_be(:milestone) { create(:milestone, project: project) } + let_it_be(:label) { create(:label, project: project) } + let_it_be(:label2) { create(:label, project: project) } + let_it_be(:issue) { create(:issue, project: project, assignees: [user], milestone: milestone) } before do stub_licensed_features(multiple_issue_assignees: false, issue_weights: false) @@ -234,6 +234,28 @@ RSpec.describe 'New/edit issue', :js do expect(page).to have_selector('.atwho-view') end + describe 'displays issue type options in the dropdown' do + before do + page.within('.issue-form') do + click_button 'Issue' + end + end + + it 'correctly displays the Issue type option with an icon', :aggregate_failures do + page.within('[data-testid="issue-type-select-dropdown"]') do + expect(page).to have_selector('[data-testid="issue-type-issue-icon"]') + expect(page).to have_content('Issue') + end + end + + it 'correctly displays the Incident type option with an icon', :aggregate_failures do + page.within('[data-testid="issue-type-select-dropdown"]') do + expect(page).to have_selector('[data-testid="issue-type-incident-icon"]') + expect(page).to have_content('Incident') + end + end + end + describe 'milestone' do let!(:milestone) { create(:milestone, title: '"><img src=x onerror=alert(document.domain)>', project: project) } diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb index 0e2ef5cc6eb..e198d9d4ebb 100644 --- a/spec/features/issues/issue_sidebar_spec.rb +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -408,7 +408,7 @@ RSpec.describe 'Issue Sidebar' do context 'sidebar', :js do it 'finds issue copy forwarding email' do - expect(find('[data-qa-selector="copy-forward-email"]').text).to eq "Issue email: #{issue.creatable_note_email_address(user)}" + expect(find('[data-qa-selector="copy-forward-email"]').text).to eq "Issue email: #{issue.creatable_note_email_address(user)}" # rubocop:disable QA/SelectorUsage end end @@ -444,7 +444,7 @@ RSpec.describe 'Issue Sidebar' do end it 'does not find issue email' do - expect(page).not_to have_selector('[data-qa-selector="copy-forward-email"]') + expect(page).not_to have_selector('[data-qa-selector="copy-forward-email"]') # rubocop:disable QA/SelectorUsage end end end diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb index 4a77e850d51..f46aa5c21b6 100644 --- a/spec/features/issues/user_creates_issue_spec.rb +++ b/spec/features/issues/user_creates_issue_spec.rb @@ -182,7 +182,7 @@ RSpec.describe "User creates issue" do end it 'does not hide the milestone select' do - expect(page).to have_selector('.qa-issuable-milestone-dropdown') + expect(page).to have_selector('.qa-issuable-milestone-dropdown') # rubocop:disable QA/SelectorUsage end end @@ -202,11 +202,11 @@ RSpec.describe "User creates issue" do end it 'shows the milestone select' do - expect(page).to have_selector('.qa-issuable-milestone-dropdown') + expect(page).to have_selector('.qa-issuable-milestone-dropdown') # rubocop:disable QA/SelectorUsage end it 'hides the weight input' do - expect(page).not_to have_selector('.qa-issuable-weight-input') + expect(page).not_to have_selector('.qa-issuable-weight-input') # rubocop:disable QA/SelectorUsage end it 'shows the incident help text' do diff --git a/spec/features/jira_connect/branches_spec.rb b/spec/features/jira_connect/branches_spec.rb new file mode 100644 index 00000000000..6fa600c6906 --- /dev/null +++ b/spec/features/jira_connect/branches_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Create GitLab branches from Jira', :js do + let_it_be(:alice) { create(:user, name: 'Alice') } + let_it_be(:bob) { create(:user, name: 'Bob') } + + let_it_be(:project1) { create(:project, :repository, namespace: alice.namespace, title: 'foo') } + let_it_be(:project2) { create(:project, :repository, namespace: alice.namespace, title: 'bar') } + let_it_be(:project3) { create(:project, namespace: bob.namespace) } + + let(:source_branch) { 'my-source-branch' } + let(:new_branch) { 'my-new-branch' } + + before do + project2.repository.add_branch(alice, source_branch, 'master') + sign_in(alice) + end + + def within_dropdown(&block) + within('.dropdown-menu', &block) + end + + it 'select project and branch and submit the form' do + visit new_jira_connect_branch_path(issue_key: 'ACME-123', issue_summary: 'My issue !@#$% title') + + expect(page).to have_field('Branch name', with: 'ACME-123-my-issue-title') + expect(page).to have_button('Create branch', disabled: true) + + # Select project1 + + click_on 'Select a project' + + within_dropdown do + expect(page).to have_text('Alice / foo') + expect(page).to have_text('Alice / bar') + expect(page).not_to have_text('Bob /') + + fill_in 'Search', with: 'foo' + + expect(page).not_to have_text('Alice / bar') + + click_on 'Alice / foo' + end + + expect(page).to have_button('Create branch', disabled: false) + + click_on 'master' + + within_dropdown do + fill_in 'Search', with: source_branch + + expect(page).not_to have_text(source_branch) + + fill_in 'Search', with: 'master' + + expect(page).to have_text('master') + end + + # Switch to project2 + + click_on 'Alice / foo' + + within_dropdown do + fill_in 'Search', with: '' + click_on 'Alice / bar' + end + + click_on 'master' + + within_dropdown do + fill_in 'Search', with: source_branch + click_on source_branch + end + + fill_in 'Branch name', with: new_branch + click_on 'Create branch' + + expect(page).to have_text('New branch was successfully created. You can now close this window and return to Jira.') + + expect(project1.commit(new_branch)).to be_nil + expect(project2.commit(new_branch)).not_to be_nil + expect(project2.commit(new_branch)).to eq(project2.commit(source_branch)) + end +end diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb index 0a2f81986be..fca5e946d0c 100644 --- a/spec/features/labels_hierarchy_spec.rb +++ b/spec/features/labels_hierarchy_spec.rb @@ -17,7 +17,6 @@ RSpec.describe 'Labels Hierarchy', :js do let!(:project_label_1) { create(:label, project: project_1, title: 'Label_4') } before do - stub_feature_flags(graphql_board_lists: false) stub_feature_flags(board_new_list: false) grandparent.add_owner(user) @@ -25,20 +24,21 @@ RSpec.describe 'Labels Hierarchy', :js do end shared_examples 'assigning labels from sidebar' do - it 'can assign all ancestors labels', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/27952' do + it 'can assign all ancestors labels' do [grandparent_group_label, parent_group_label, project_label_1].each do |label| page.within('.block.labels') do - find('.edit-link').click + click_on 'Edit' end wait_for_requests find('a.label-item', text: label.title).click - find('.dropdown-menu-close-icon').click + wait_for_requests + click_on 'Close' wait_for_requests - expect(page).to have_selector('.badge', text: label.title) + expect(page).to have_selector('.gl-label', text: label.title) end end @@ -215,6 +215,44 @@ RSpec.describe 'Labels Hierarchy', :js do end end + context 'issuable sidebar when graphql_board_lists FF disabled' do + let!(:issue) { create(:issue, project: project_1) } + + before do + stub_feature_flags(graphql_board_lists: false) + end + + context 'on project board issue sidebar' do + before do + project_1.add_developer(user) + board = create(:board, project: project_1) + + visit project_board_path(project_1, board) + + wait_for_requests + + find('.board-card').click + end + + it_behaves_like 'assigning labels from sidebar' + end + + context 'on group board issue sidebar' do + before do + parent.add_developer(user) + board = create(:board, group: parent) + + visit group_board_path(parent, board) + + wait_for_requests + + find('.board-card').click + end + + it_behaves_like 'assigning labels from sidebar' + end + end + context 'issuable filtering' do let!(:labeled_issue) { create(:labeled_issue, project: project_1, labels: [grandparent_group_label, parent_group_label, project_label_1]) } let!(:issue) { create(:issue, project: project_1) } @@ -302,6 +340,34 @@ RSpec.describe 'Labels Hierarchy', :js do let(:board) { create(:board, group: parent) } before do + parent.add_developer(user) + visit group_board_path(parent, board) + find('.js-new-board-list').click + wait_for_requests + end + + context 'when graphql_board_lists FF enabled' do + it 'creates lists from all ancestor group labels' do + [grandparent_group_label, parent_group_label].each do |label| + find('a', text: label.title).click + end + + wait_for_requests + + expect(page).to have_selector('.board-title-text', text: grandparent_group_label.title) + expect(page).to have_selector('.board-title-text', text: parent_group_label.title) + end + + it 'does not create lists from descendant groups' do + expect(page).not_to have_selector('a', text: child_group_label.title) + end + end + end + + context 'when graphql_board_lists FF disabled' do + let(:board) { create(:board, group: parent) } + + before do stub_feature_flags(graphql_board_lists: false) parent.add_developer(user) visit group_board_path(parent, board) diff --git a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb index 9e1b0135932..af5ba14e310 100644 --- a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb +++ b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb @@ -64,7 +64,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do context 'when enabled after it was previously canceled' do before do click_button "Merge when pipeline succeeds" - click_link "Cancel" + click_button "Cancel auto-merge" wait_for_requests @@ -87,7 +87,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do before do merge_request.merge_params['force_remove_source_branch'] = '0' merge_request.save! - click_link "Cancel" + click_button "Cancel auto-merge" end it_behaves_like 'Merge when pipeline succeeds activator' @@ -114,7 +114,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do end it 'allows to cancel the automatic merge' do - click_link "Cancel" + click_button "Cancel auto-merge" expect(page).to have_button "Merge when pipeline succeeds" @@ -124,7 +124,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do end it 'allows to delete source branch' do - click_link "Delete source branch" + click_button "Delete source branch" expect(page).to have_content "The source branch will be deleted" end diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb index 73e628bda98..8343e04aef1 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 @@ -63,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 - find('button[data-qa-selector="resolve_discussion_button"]').click + find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage end expect(page).to have_selector('.discussion-body', visible: false) @@ -80,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 - find('button[data-qa-selector="resolve_discussion_button"]').click + find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage click_button 'Unresolve thread' end @@ -92,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 - find('button[data-qa-selector="resolve_discussion_button"]').click + find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage end visit_merge_request @@ -193,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 - find('button[data-qa-selector="resolve_discussion_button"]').click + find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage end page.within '.line-resolve-all-container' do @@ -230,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 - find('button[data-qa-selector="resolve_discussion_button"]').click + find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage end expect(page).to have_selector('.discussion-next-btn', visible: false) @@ -326,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 - find('button[data-qa-selector="resolve_discussion_button"]').click + find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage end end @@ -338,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 - find('button[data-qa-selector="resolve_discussion_button"]').click + find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage end page.within '.line-resolve-all-container' do @@ -410,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 - find('button[data-qa-selector="resolve_discussion_button"]').click + find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage end page.within '.diff-content .note' do @@ -425,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 - find('button[data-qa-selector="resolve_discussion_button"]').click + find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage click_button 'Unresolve thread' end @@ -453,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 - find('button[data-qa-selector="resolve_discussion_button"]').click + find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find_field('Reply…').click diff --git a/spec/features/merge_request/user_sees_closing_issues_message_spec.rb b/spec/features/merge_request/user_sees_closing_issues_message_spec.rb index d6cdc15005b..7b7fff5c936 100644 --- a/spec/features/merge_request/user_sees_closing_issues_message_spec.rb +++ b/spec/features/merge_request/user_sees_closing_issues_message_spec.rb @@ -31,7 +31,7 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do let(:merge_request_description) { "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" } it 'does not display closing issue message' do - expect(page).to have_content("Closes #{issue_1.to_reference} and #{issue_2.to_reference}") + expect(page).to have_content("Closes issues #{issue_1.to_reference} and #{issue_2.to_reference}") end end @@ -39,7 +39,7 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do let(:merge_request_description) { "Description\n\nRefers to #{issue_1.to_reference} and #{issue_2.to_reference}" } it 'does not display closing issue message' do - expect(page).to have_content("Mentions #{issue_1.to_reference} and #{issue_2.to_reference}") + expect(page).to have_content("Mentions issues #{issue_1.to_reference} and #{issue_2.to_reference}") end end @@ -47,8 +47,8 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do let(:merge_request_title) { "closes #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" } it 'does not display closing issue message' do - expect(page).to have_content("Closes #{issue_1.to_reference}") - expect(page).to have_content("Mentions #{issue_2.to_reference}") + expect(page).to have_content("Closes issue #{issue_1.to_reference}") + expect(page).to have_content("Mentions issue #{issue_2.to_reference}") end end @@ -56,7 +56,7 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do let(:merge_request_title) { "closing #{issue_1.to_reference}, #{issue_2.to_reference}" } it 'does not display closing issue message' do - expect(page).to have_content("Closes #{issue_1.to_reference} and #{issue_2.to_reference}") + expect(page).to have_content("Closes issues #{issue_1.to_reference} and #{issue_2.to_reference}") end end @@ -64,7 +64,7 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do let(:merge_request_title) { "Refers to #{issue_1.to_reference} and #{issue_2.to_reference}" } it 'does not display closing issue message' do - expect(page).to have_content("Mentions #{issue_1.to_reference} and #{issue_2.to_reference}") + expect(page).to have_content("Mentions issues #{issue_1.to_reference} and #{issue_2.to_reference}") end end @@ -72,8 +72,8 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do let(:merge_request_title) { "closes #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" } it 'does not display closing issue message' do - expect(page).to have_content("Closes #{issue_1.to_reference}") - expect(page).to have_content("Mentions #{issue_2.to_reference}") + expect(page).to have_content("Closes issue #{issue_1.to_reference}") + expect(page).to have_content("Mentions issue #{issue_2.to_reference}") end end end diff --git a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb index 85eb956033b..2a49109d360 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 @@ -43,12 +43,14 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', let!(:push_pipeline) do Ci::CreatePipelineService.new(project, user, ref: 'feature') - .execute(:push) + .execute(:push) + .payload end let!(:detached_merge_request_pipeline) do Ci::CreatePipelineService.new(project, user, ref: 'feature') - .execute(:merge_request_event, merge_request: merge_request) + .execute(:merge_request_event, merge_request: merge_request) + .payload end before do @@ -77,12 +79,14 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', context 'when a user updated a merge request in the parent project', :sidekiq_might_not_need_inline do let!(:push_pipeline_2) do Ci::CreatePipelineService.new(project, user, ref: 'feature') - .execute(:push) + .execute(:push) + .payload end let!(:detached_merge_request_pipeline_2) do Ci::CreatePipelineService.new(project, user, ref: 'feature') - .execute(:merge_request_event, merge_request: merge_request) + .execute(:merge_request_event, merge_request: merge_request) + .payload end before do @@ -147,7 +151,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', context 'when detached merge request pipeline is pending' do it 'waits the head pipeline' do expect(page).to have_content('to be merged automatically when the pipeline succeeds') - expect(page).to have_link('Cancel') + expect(page).to have_button('Cancel auto-merge') end end @@ -174,7 +178,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', it 'waits the head pipeline' do expect(page).to have_content('to be merged automatically when the pipeline succeeds') - expect(page).to have_link('Cancel') + expect(page).to have_button('Cancel auto-merge') end end end @@ -222,12 +226,14 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', let!(:push_pipeline) do Ci::CreatePipelineService.new(forked_project, user2, ref: 'feature') - .execute(:push) + .execute(:push) + .payload end let!(:detached_merge_request_pipeline) do Ci::CreatePipelineService.new(forked_project, user2, ref: 'feature') - .execute(:merge_request_event, merge_request: merge_request) + .execute(:merge_request_event, merge_request: merge_request) + .payload end let(:forked_project) { fork_project(project, user2, repository: true) } @@ -267,12 +273,14 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', context 'when a user updated a merge request from a forked project to the parent project' do let!(:push_pipeline_2) do Ci::CreatePipelineService.new(forked_project, user2, ref: 'feature') - .execute(:push) + .execute(:push) + .payload end let!(:detached_merge_request_pipeline_2) do Ci::CreatePipelineService.new(forked_project, user2, ref: 'feature') - .execute(:merge_request_event, merge_request: merge_request) + .execute(:merge_request_event, merge_request: merge_request) + .payload end before do @@ -369,7 +377,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', context 'when detached merge request pipeline is pending' do it 'waits the head pipeline' do expect(page).to have_content('to be merged automatically when the pipeline succeeds') - expect(page).to have_link('Cancel') + expect(page).to have_button('Cancel auto-merge') end end @@ -395,7 +403,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', it 'waits the head pipeline' do expect(page).to have_content('to be merged automatically when the pipeline succeeds') - expect(page).to have_link('Cancel') + expect(page).to have_button('Cancel auto-merge') end end end diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb index a6c8b10f5ca..4967f58528e 100644 --- a/spec/features/merge_request/user_sees_pipelines_spec.rb +++ b/spec/features/merge_request/user_sees_pipelines_spec.rb @@ -245,7 +245,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js do threads << Thread.new do Sidekiq::Worker.skipping_transaction_check do - @pipeline = Ci::CreatePipelineService.new(project, user, build_push_data).execute(:push) + @pipeline = Ci::CreatePipelineService.new(project, user, build_push_data).execute(:push).payload end end diff --git a/spec/features/merge_request/user_views_diffs_spec.rb b/spec/features/merge_request/user_views_diffs_spec.rb index 09dfe41a718..208ed1f01e7 100644 --- a/spec/features/merge_request/user_views_diffs_spec.rb +++ b/spec/features/merge_request/user_views_diffs_spec.rb @@ -60,7 +60,7 @@ RSpec.describe 'User views diffs', :js do expect(page).not_to have_selector('.mr-loading-status .loading', visible: true) end - it 'expands all diffs' do + it 'expands all diffs', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/333628' do first('.diff-toggle-caret').click expect(page).to have_button('Expand all') diff --git a/spec/features/merge_requests/user_lists_merge_requests_spec.rb b/spec/features/merge_requests/user_lists_merge_requests_spec.rb index ab6242784fe..f96717970bf 100644 --- a/spec/features/merge_requests/user_lists_merge_requests_spec.rb +++ b/spec/features/merge_requests/user_lists_merge_requests_spec.rb @@ -23,7 +23,7 @@ RSpec.describe 'Merge requests > User lists merge requests' do milestone: create(:milestone, project: project, due_date: '2013-12-11'), created_at: 1.minute.ago, updated_at: 1.minute.ago) - @fix.metrics.update_column(:merged_at, 10.seconds.ago) + @fix.metrics.update!(merged_at: 10.seconds.ago, latest_closed_at: 10.seconds.ago) @markdown = create(:merge_request, title: 'markdown', @@ -34,7 +34,7 @@ RSpec.describe 'Merge requests > User lists merge requests' do milestone: create(:milestone, project: project, due_date: '2013-12-12'), created_at: 2.minutes.ago, updated_at: 2.minutes.ago) - @markdown.metrics.update_column(:merged_at, 50.seconds.ago) + @markdown.metrics.update!(merged_at: 10.minutes.ago, latest_closed_at: 10.seconds.ago) @merge_test = create(:merge_request, title: 'merge-test', @@ -42,7 +42,15 @@ RSpec.describe 'Merge requests > User lists merge requests' do source_branch: 'merge-test', created_at: 3.minutes.ago, updated_at: 10.seconds.ago) - @merge_test.metrics.update_column(:merged_at, 10.seconds.ago) + @merge_test.metrics.update!(merged_at: 10.seconds.ago, latest_closed_at: 10.seconds.ago) + + @feature = create(:merge_request, + title: 'feature', + source_project: project, + source_branch: 'feautre', + created_at: 2.minutes.ago, + updated_at: 1.minute.ago) + @feature.metrics.update!(merged_at: 10.seconds.ago, latest_closed_at: 10.minutes.ago) end context 'merge request reviewers' do @@ -71,9 +79,10 @@ RSpec.describe 'Merge requests > User lists merge requests' do expect(current_path).to eq(project_merge_requests_path(project)) expect(page).to have_content 'merge-test' + expect(page).to have_content 'feature' expect(page).not_to have_content 'fix' expect(page).not_to have_content 'markdown' - expect(count_merge_requests).to eq(1) + expect(count_merge_requests).to eq(2) end it 'filters on a specific assignee' do @@ -90,28 +99,35 @@ RSpec.describe 'Merge requests > User lists merge requests' do expect(first_merge_request).to include('fix') expect(last_merge_request).to include('merge-test') - expect(count_merge_requests).to eq(3) + expect(count_merge_requests).to eq(4) end it 'sorts by last updated' do visit_merge_requests(project, sort: sort_value_recently_updated) expect(first_merge_request).to include('merge-test') - expect(count_merge_requests).to eq(3) + expect(count_merge_requests).to eq(4) end it 'sorts by milestone' do visit_merge_requests(project, sort: sort_value_milestone) expect(first_merge_request).to include('fix') - expect(count_merge_requests).to eq(3) + expect(count_merge_requests).to eq(4) end it 'sorts by merged at' do visit_merge_requests(project, sort: sort_value_merged_date) expect(first_merge_request).to include('markdown') - expect(count_merge_requests).to eq(3) + expect(count_merge_requests).to eq(4) + end + + it 'sorts by closed at' do + visit_merge_requests(project, sort: sort_value_closed_date) + + expect(first_merge_request).to include('feature') + expect(count_merge_requests).to eq(4) end it 'filters on one label and sorts by due date' do diff --git a/spec/features/nav/top_nav_responsive_spec.rb b/spec/features/nav/top_nav_responsive_spec.rb index dfe3e76f172..5c6a12a37a3 100644 --- a/spec/features/nav/top_nav_responsive_spec.rb +++ b/spec/features/nav/top_nav_responsive_spec.rb @@ -8,8 +8,6 @@ RSpec.describe 'top nav responsive', :js do let_it_be(:user) { create(:user) } before do - stub_feature_flags(combined_menu: true) - sign_in(user) visit explore_projects_path diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index 0f453f1c1e5..9a261c6d9c8 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -74,8 +74,6 @@ RSpec.describe 'Profile account page', :js do expect(find('#feed_token').value).not_to eq(previous_token) end - - expect(page).to have_content 'Feed token was successfully reset' end end diff --git a/spec/features/profiles/user_manages_applications_spec.rb b/spec/features/profiles/user_manages_applications_spec.rb index 22eed748c00..c76ef2613fd 100644 --- a/spec/features/profiles/user_manages_applications_spec.rb +++ b/spec/features/profiles/user_manages_applications_spec.rb @@ -3,55 +3,12 @@ require 'spec_helper' RSpec.describe 'User manages applications' do - let(:user) { create(:user) } + let_it_be(:user) { create(:user) } + let_it_be(:new_application_path) { applications_profile_path } before do sign_in(user) - visit applications_profile_path end - it 'manages applications' do - expect(page).to have_content 'Add new application' - - fill_in :doorkeeper_application_name, with: 'test' - fill_in :doorkeeper_application_redirect_uri, with: 'https://test.com' - check :doorkeeper_application_scopes_read_user - click_on 'Save application' - - expect(page).to have_content 'Application: test' - expect(page).to have_content 'Application ID' - expect(page).to have_content 'Secret' - expect(page).to have_content 'Confidential Yes' - - click_on 'Edit' - - expect(page).to have_content 'Edit application' - fill_in :doorkeeper_application_name, with: 'test_changed' - uncheck :doorkeeper_application_confidential - click_on 'Save application' - - expect(page).to have_content 'test_changed' - expect(page).to have_content 'Application ID' - expect(page).to have_content 'Secret' - expect(page).to have_content 'Confidential No' - - visit applications_profile_path - - page.within '.oauth-applications' do - click_on 'Destroy' - end - expect(page.find('.oauth-applications')).not_to have_content 'test_changed' - end - - context 'when scopes are blank' do - it 'returns an error' do - expect(page).to have_content 'Add new application' - - fill_in :doorkeeper_application_name, with: 'test' - fill_in :doorkeeper_application_redirect_uri, with: 'https://test.com' - click_on 'Save application' - - expect(page).to have_content("Scopes can't be blank") - end - end + include_examples 'manage applications' end diff --git a/spec/features/project_variables_spec.rb b/spec/features/project_variables_spec.rb index 62565eaabe1..5139c724d82 100644 --- a/spec/features/project_variables_spec.rb +++ b/spec/features/project_variables_spec.rb @@ -21,7 +21,7 @@ RSpec.describe 'Project variables', :js do click_button('Add variable') page.within('#add-ci-variable') do - find('[data-qa-selector="ci_variable_key_field"] input').set('akey') + find('[data-qa-selector="ci_variable_key_field"] input').set('akey') # rubocop:disable QA/SelectorUsage find('#ci-variable-value').set('akey_value') find('[data-testid="environment-scope"]').click find('[data-testid="ci-environment-search"]').set('review/*') diff --git a/spec/features/projects/activity/user_sees_design_activity_spec.rb b/spec/features/projects/activity/user_sees_design_activity_spec.rb index 389e86299e5..70153921b82 100644 --- a/spec/features/projects/activity/user_sees_design_activity_spec.rb +++ b/spec/features/projects/activity/user_sees_design_activity_spec.rb @@ -34,26 +34,26 @@ RSpec.describe 'Projects > Activity > User sees design Activity', :js do visit activity_project_path(project) expect(page).to have_content('joined project') - expect(page).to have_content(design_activity(uploader, 'uploaded')) - expect(page).to have_content(design_activity(editor, 'revised')) - expect(page).to have_content(design_activity(deleter, 'deleted')) + expect(page).to have_content(design_activity(uploader, 'added')) + expect(page).to have_content(design_activity(editor, 'updated')) + expect(page).to have_content(design_activity(deleter, 'removed')) end it 'allows filtering out the design events', :aggregate_failures do visit activity_project_path(project, event_filter: EventFilter::ISSUE) - expect(page).not_to have_content(design_activity(uploader, 'uploaded')) - expect(page).not_to have_content(design_activity(editor, 'revised')) - expect(page).not_to have_content(design_activity(deleter, 'deleted')) + expect(page).not_to have_content(design_activity(uploader, 'added')) + expect(page).not_to have_content(design_activity(editor, 'updated')) + expect(page).not_to have_content(design_activity(deleter, 'removed')) end it 'allows filtering in the design events', :aggregate_failures do visit activity_project_path(project, event_filter: EventFilter::DESIGNS) expect(page).not_to have_content('joined project') - expect(page).to have_content(design_activity(uploader, 'uploaded')) - expect(page).to have_content(design_activity(editor, 'revised')) - expect(page).to have_content(design_activity(deleter, 'deleted')) + expect(page).to have_content(design_activity(uploader, 'added')) + expect(page).to have_content(design_activity(editor, 'updated')) + expect(page).to have_content(design_activity(deleter, 'removed')) end end diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb index 595304789a6..8281e82959b 100644 --- a/spec/features/projects/blobs/blob_show_spec.rb +++ b/spec/features/projects/blobs/blob_show_spec.rb @@ -13,6 +13,22 @@ RSpec.describe 'File blob', :js do wait_for_requests end + def create_file(file_name, content) + project.add_maintainer(project.creator) + + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add #{file_name}", + file_path: file_name, + file_content: <<-SPEC.strip_heredoc + #{content} + SPEC + ).execute + end + context 'Ruby file' do before do visit_blob('files/ruby/popen.rb') @@ -121,7 +137,7 @@ RSpec.describe 'File blob', :js do context 'when ref switch' do def switch_ref_to(ref_name) - first('.qa-branches-select').click + first('.qa-branches-select').click # rubocop:disable QA/SelectorUsage page.within '.project-refs-form' do click_link ref_name @@ -584,94 +600,483 @@ RSpec.describe 'File blob', :js do end end - describe '.gitlab-ci.yml' do + context 'files with auxiliary viewers' do before do - project.add_maintainer(project.creator) + stub_feature_flags(refactor_blob_viewer: true) + end - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add .gitlab-ci.yml", - file_path: '.gitlab-ci.yml', - file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) - ).execute + describe '.gitlab-ci.yml' do + before do + project.add_maintainer(project.creator) + + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add .gitlab-ci.yml", + file_path: '.gitlab-ci.yml', + file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) + ).execute + + visit_blob('.gitlab-ci.yml') + end - visit_blob('.gitlab-ci.yml') + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that configuration is valid + expect(page).to have_content('This GitLab CI configuration is valid.') + + # shows a learn more link + expect(page).to have_link('Learn more') + end + end end - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that configuration is valid - expect(page).to have_content('This GitLab CI configuration is valid.') + describe '.gitlab/route-map.yml' do + before do + project.add_maintainer(project.creator) + + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add .gitlab/route-map.yml", + file_path: '.gitlab/route-map.yml', + file_content: <<-MAP.strip_heredoc + # Team data + - source: 'data/team.yml' + public: 'team/' + MAP + ).execute + + visit_blob('.gitlab/route-map.yml') + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that map is valid + expect(page).to have_content('This Route Map is valid.') - # shows a learn more link - expect(page).to have_link('Learn more') + # shows a learn more link + expect(page).to have_link('Learn more') + end end end - end - describe '.gitlab/route-map.yml' do - before do - project.add_maintainer(project.creator) + describe '.gitlab/dashboards/custom-dashboard.yml' do + before do + project.add_maintainer(project.creator) - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add .gitlab/route-map.yml", - file_path: '.gitlab/route-map.yml', - file_content: <<-MAP.strip_heredoc - # Team data - - source: 'data/team.yml' - public: 'team/' - MAP - ).execute + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add .gitlab/dashboards/custom-dashboard.yml", + file_path: '.gitlab/dashboards/custom-dashboard.yml', + file_content: file_content + ).execute + end + + context 'with metrics_dashboard_exhaustive_validations feature flag off' do + before do + stub_feature_flags(metrics_dashboard_exhaustive_validations: false) + visit_blob('.gitlab/dashboards/custom-dashboard.yml') + end + + context 'valid dashboard file' do + let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } + + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is valid + expect(page).to have_content('Metrics Dashboard YAML definition is valid.') + + # shows a learn more link + expect(page).to have_link('Learn more') + end + end + end + + context 'invalid dashboard file' do + let(:file_content) { "dashboard: 'invalid'" } + + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is invalid + expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') + expect(page).to have_content("panel_groups: should be an array of panel_groups objects") + + # shows a learn more link + expect(page).to have_link('Learn more') + end + end + end + end + + context 'with metrics_dashboard_exhaustive_validations feature flag on' do + before do + stub_feature_flags(metrics_dashboard_exhaustive_validations: true) + visit_blob('.gitlab/dashboards/custom-dashboard.yml') + end + + context 'valid dashboard file' do + let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } - visit_blob('.gitlab/route-map.yml') + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is valid + expect(page).to have_content('Metrics Dashboard YAML definition is valid.') + + # shows a learn more link + expect(page).to have_link('Learn more') + end + end + end + + context 'invalid dashboard file' do + let(:file_content) { "dashboard: 'invalid'" } + + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is invalid + expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') + expect(page).to have_content("root is missing required keys: panel_groups") + + # shows a learn more link + expect(page).to have_link('Learn more') + end + end + end + end end - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that map is valid - expect(page).to have_content('This Route Map is valid.') + context 'LICENSE' do + before do + visit_blob('LICENSE') + end - # shows a learn more link - expect(page).to have_link('Learn more') + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows license + expect(page).to have_content('This project is licensed under the MIT License.') + + # shows a learn more link + expect(page).to have_link('Learn more', href: 'http://choosealicense.com/licenses/mit/') + end end end - end - describe '.gitlab/dashboards/custom-dashboard.yml' do - before do - project.add_maintainer(project.creator) + context '*.gemspec' do + before do + project.add_maintainer(project.creator) - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add .gitlab/dashboards/custom-dashboard.yml", - file_path: '.gitlab/dashboards/custom-dashboard.yml', - file_content: file_content - ).execute + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add activerecord.gemspec", + file_path: 'activerecord.gemspec', + file_content: <<-SPEC.strip_heredoc + Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = "activerecord" + end + SPEC + ).execute + + visit_blob('activerecord.gemspec') + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows names of dependency manager and package + expect(page).to have_content('This project manages its dependencies using RubyGems.') + + # shows a learn more link + expect(page).to have_link('Learn more', href: 'https://rubygems.org/') + end + end end - context 'with metrics_dashboard_exhaustive_validations feature flag off' do + context 'CONTRIBUTING.md' do before do - stub_feature_flags(metrics_dashboard_exhaustive_validations: false) - visit_blob('.gitlab/dashboards/custom-dashboard.yml') + file_name = 'CONTRIBUTING.md' + + create_file(file_name, '## Contribution guidelines') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("After you've reviewed these contribution guidelines, you'll be all set to contribute to this project.") + end + end + end + + context 'CHANGELOG.md' do + before do + file_name = 'CHANGELOG.md' + + create_file(file_name, '## Changelog for v1.0.0') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("To find the state of this project's repository at the time of any of these versions, check out the tags.") + end + end + end + + context 'Cargo.toml' do + before do + file_name = 'Cargo.toml' + + create_file(file_name, ' + [package] + name = "hello_world" # the name of the package + version = "0.1.0" # the current version, obeying semver + authors = ["Alice <a@example.com>", "Bob <b@example.com>"] + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Cargo.") + end + end + end + + context 'Cartfile' do + before do + file_name = 'Cartfile' + + create_file(file_name, ' + gitlab "Alamofire/Alamofire" == 4.9.0 + gitlab "Alamofire/AlamofireImage" ~> 3.4 + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Carthage.") + end + end + end + + context 'composer.json' do + before do + file_name = 'composer.json' + + create_file(file_name, ' + { + "license": "MIT" + } + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Composer.") + end end + end + + context 'Gemfile' do + before do + file_name = 'Gemfile' + + create_file(file_name, ' + source "https://rubygems.org" - context 'valid dashboard file' do - let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } + # Gems here + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Bundler.") + end + end + end + + context 'Godeps.json' do + before do + file_name = 'Godeps.json' + + create_file(file_name, ' + { + "GoVersion": "go1.6" + } + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using godep.") + end + end + end + + context 'go.mod' do + before do + file_name = 'go.mod' + + create_file(file_name, ' + module example.com/mymodule + + go 1.14 + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Go Modules.") + end + end + end + + context 'package.json' do + before do + file_name = 'package.json' + + create_file(file_name, ' + { + "name": "my-awesome-package", + "version": "1.0.0" + } + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using npm.") + end + end + end + + context 'podfile' do + before do + file_name = 'podfile' + + create_file(file_name, 'platform :ios, "8.0"') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") + end + end + end + + context 'test.podspec' do + before do + file_name = 'test.podspec' + + create_file(file_name, ' + Pod::Spec.new do |s| + s.name = "TensorFlowLiteC" + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") + end + end + end + + context 'JSON.podspec.json' do + before do + file_name = 'JSON.podspec.json' + + create_file(file_name, ' + { + "name": "JSON" + } + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") + end + end + end + + context 'requirements.txt' do + before do + file_name = 'requirements.txt' + + create_file(file_name, 'Project requirements') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using pip.") + end + end + end + + context 'yarn.lock' do + before do + file_name = 'yarn.lock' + + create_file(file_name, ' + # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. + # yarn lockfile v1 + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Yarn.") + end + end + end + + context 'when refactor_blob_viewer is disabled' do + before do + stub_feature_flags(refactor_blob_viewer: false) + end + + describe '.gitlab-ci.yml' do + before do + project.add_maintainer(project.creator) + + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add .gitlab-ci.yml", + file_path: '.gitlab-ci.yml', + file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) + ).execute + + visit_blob('.gitlab-ci.yml') + end it 'displays an auxiliary viewer' do aggregate_failures do - # shows that dashboard yaml is valid - expect(page).to have_content('Metrics Dashboard YAML definition is valid.') + # shows that configuration is valid + expect(page).to have_content('This GitLab CI configuration is valid.') # shows a learn more link expect(page).to have_link('Learn more') @@ -679,104 +1084,422 @@ RSpec.describe 'File blob', :js do end end - context 'invalid dashboard file' do - let(:file_content) { "dashboard: 'invalid'" } + describe '.gitlab/route-map.yml' do + before do + project.add_maintainer(project.creator) + + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add .gitlab/route-map.yml", + file_path: '.gitlab/route-map.yml', + file_content: <<-MAP.strip_heredoc + # Team data + - source: 'data/team.yml' + public: 'team/' + MAP + ).execute + + visit_blob('.gitlab/route-map.yml') + end it 'displays an auxiliary viewer' do aggregate_failures do - # shows that dashboard yaml is invalid - expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') - expect(page).to have_content("panel_groups: should be an array of panel_groups objects") + # shows that map is valid + expect(page).to have_content('This Route Map is valid.') # shows a learn more link expect(page).to have_link('Learn more') end end end - end - context 'with metrics_dashboard_exhaustive_validations feature flag on' do - before do - stub_feature_flags(metrics_dashboard_exhaustive_validations: true) - visit_blob('.gitlab/dashboards/custom-dashboard.yml') + describe '.gitlab/dashboards/custom-dashboard.yml' do + before do + project.add_maintainer(project.creator) + + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add .gitlab/dashboards/custom-dashboard.yml", + file_path: '.gitlab/dashboards/custom-dashboard.yml', + file_content: file_content + ).execute + end + + context 'with metrics_dashboard_exhaustive_validations feature flag off' do + before do + stub_feature_flags(metrics_dashboard_exhaustive_validations: false) + visit_blob('.gitlab/dashboards/custom-dashboard.yml') + end + + context 'valid dashboard file' do + let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } + + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is valid + expect(page).to have_content('Metrics Dashboard YAML definition is valid.') + + # shows a learn more link + expect(page).to have_link('Learn more') + end + end + end + + context 'invalid dashboard file' do + let(:file_content) { "dashboard: 'invalid'" } + + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is invalid + expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') + expect(page).to have_content("panel_groups: should be an array of panel_groups objects") + + # shows a learn more link + expect(page).to have_link('Learn more') + end + end + end + end + + context 'with metrics_dashboard_exhaustive_validations feature flag on' do + before do + stub_feature_flags(metrics_dashboard_exhaustive_validations: true) + visit_blob('.gitlab/dashboards/custom-dashboard.yml') + end + + context 'valid dashboard file' do + let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } + + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is valid + expect(page).to have_content('Metrics Dashboard YAML definition is valid.') + + # shows a learn more link + expect(page).to have_link('Learn more') + end + end + end + + context 'invalid dashboard file' do + let(:file_content) { "dashboard: 'invalid'" } + + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is invalid + expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') + expect(page).to have_content("root is missing required keys: panel_groups") + + # shows a learn more link + expect(page).to have_link('Learn more') + end + end + end + end end - context 'valid dashboard file' do - let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } + context 'LICENSE' do + before do + visit_blob('LICENSE') + end it 'displays an auxiliary viewer' do aggregate_failures do - # shows that dashboard yaml is valid - expect(page).to have_content('Metrics Dashboard YAML definition is valid.') + # shows license + expect(page).to have_content('This project is licensed under the MIT License.') # shows a learn more link - expect(page).to have_link('Learn more') + expect(page).to have_link('Learn more', href: 'http://choosealicense.com/licenses/mit/') end end end - context 'invalid dashboard file' do - let(:file_content) { "dashboard: 'invalid'" } + context '*.gemspec' do + before do + project.add_maintainer(project.creator) + + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add activerecord.gemspec", + file_path: 'activerecord.gemspec', + file_content: <<-SPEC.strip_heredoc + Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = "activerecord" + end + SPEC + ).execute + + visit_blob('activerecord.gemspec') + end it 'displays an auxiliary viewer' do aggregate_failures do - # shows that dashboard yaml is invalid - expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') - expect(page).to have_content("root is missing required keys: panel_groups") + # shows names of dependency manager and package + expect(page).to have_content('This project manages its dependencies using RubyGems.') # shows a learn more link - expect(page).to have_link('Learn more') + expect(page).to have_link('Learn more', href: 'https://rubygems.org/') end end end - end - end - context 'LICENSE' do - before do - visit_blob('LICENSE') - end + context 'CONTRIBUTING.md' do + before do + file_name = 'CONTRIBUTING.md' - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows license - expect(page).to have_content('This project is licensed under the MIT License.') + create_file(file_name, '## Contribution guidelines') + visit_blob(file_name) + end - # shows a learn more link - expect(page).to have_link('Learn more', href: 'http://choosealicense.com/licenses/mit/') + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("After you've reviewed these contribution guidelines, you'll be all set to contribute to this project.") + end + end end - end - end - context '*.gemspec' do - before do - project.add_maintainer(project.creator) + context 'CHANGELOG.md' do + before do + file_name = 'CHANGELOG.md' - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add activerecord.gemspec", - file_path: 'activerecord.gemspec', - file_content: <<-SPEC.strip_heredoc - Gem::Specification.new do |s| - s.platform = Gem::Platform::RUBY - s.name = "activerecord" - end - SPEC - ).execute + create_file(file_name, '## Changelog for v1.0.0') + visit_blob(file_name) + end - visit_blob('activerecord.gemspec') - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("To find the state of this project's repository at the time of any of these versions, check out the tags.") + end + end + end - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows names of dependency manager and package - expect(page).to have_content('This project manages its dependencies using RubyGems.') + context 'Cargo.toml' do + before do + file_name = 'Cargo.toml' + + create_file(file_name, ' + [package] + name = "hello_world" # the name of the package + version = "0.1.0" # the current version, obeying semver + authors = ["Alice <a@example.com>", "Bob <b@example.com>"] + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Cargo.") + end + end + end + + context 'Cartfile' do + before do + file_name = 'Cartfile' + + create_file(file_name, ' + gitlab "Alamofire/Alamofire" == 4.9.0 + gitlab "Alamofire/AlamofireImage" ~> 3.4 + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Carthage.") + end + end + end + + context 'composer.json' do + before do + file_name = 'composer.json' + + create_file(file_name, ' + { + "license": "MIT" + } + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Composer.") + end + end + end + + context 'Gemfile' do + before do + file_name = 'Gemfile' + + create_file(file_name, ' + source "https://rubygems.org" + + # Gems here + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Bundler.") + end + end + end + + context 'Godeps.json' do + before do + file_name = 'Godeps.json' + + create_file(file_name, ' + { + "GoVersion": "go1.6" + } + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using godep.") + end + end + end + + context 'go.mod' do + before do + file_name = 'go.mod' + + create_file(file_name, ' + module example.com/mymodule - # shows a learn more link - expect(page).to have_link('Learn more', href: 'https://rubygems.org/') + go 1.14 + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Go Modules.") + end + end + end + + context 'package.json' do + before do + file_name = 'package.json' + + create_file(file_name, ' + { + "name": "my-awesome-package", + "version": "1.0.0" + } + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using npm.") + end + end + end + + context 'podfile' do + before do + file_name = 'podfile' + + create_file(file_name, 'platform :ios, "8.0"') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") + end + end + end + + context 'test.podspec' do + before do + file_name = 'test.podspec' + + create_file(file_name, ' + Pod::Spec.new do |s| + s.name = "TensorFlowLiteC" + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") + end + end + end + + context 'JSON.podspec.json' do + before do + file_name = 'JSON.podspec.json' + + create_file(file_name, ' + { + "name": "JSON" + } + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") + end + end + end + + context 'requirements.txt' do + before do + file_name = 'requirements.txt' + + create_file(file_name, 'Project requirements') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using pip.") + end + end + end + + context 'yarn.lock' do + before do + file_name = 'yarn.lock' + + create_file(file_name, ' + # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. + # yarn lockfile v1 + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Yarn.") + end + end end end end diff --git a/spec/features/projects/ci/editor_spec.rb b/spec/features/projects/ci/editor_spec.rb index c0cc12eac66..192bccd6f6e 100644 --- a/spec/features/projects/ci/editor_spec.rb +++ b/spec/features/projects/ci/editor_spec.rb @@ -5,17 +5,55 @@ require 'spec_helper' RSpec.describe 'Pipeline Editor', :js do include Spec::Support::Helpers::Features::SourceEditorSpecHelpers - let(:project) { create(:project, :repository) } + let(:project) { create(:project_empty_repo, :public) } let(:user) { create(:user) } + let(:default_branch) { 'main' } + let(:other_branch) { 'test' } + before do sign_in(user) project.add_developer(user) + project.repository.create_file(user, project.ci_config_path_or_default, 'Default Content', message: 'Create CI file for main', branch_name: default_branch) + project.repository.create_file(user, project.ci_config_path_or_default, 'Other Content', message: 'Create CI file for test', branch_name: other_branch) + visit project_ci_pipeline_editor_path(project) + wait_for_requests end it 'user sees the Pipeline Editor page' do expect(page).to have_content('Pipeline Editor') end + + context 'branch switcher' do + before do + stub_feature_flags(pipeline_editor_branch_switcher: true) + end + + def switch_to_branch(branch) + find('[data-testid="branch-selector"]').click + + page.within '[data-testid="branch-selector"]' do + click_button branch + wait_for_requests + end + end + + it 'displays current branch' do + page.within('[data-testid="branch-selector"]') do + expect(page).to have_content(default_branch) + expect(page).not_to have_content(other_branch) + end + end + + it 'displays updated current branch after switching branches' do + switch_to_branch(other_branch) + + page.within('[data-testid="branch-selector"]') do + expect(page).to have_content(other_branch) + expect(page).not_to have_content(default_branch) + end + end + end end diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb index 6de02556175..57b35d81bb8 100644 --- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb +++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb @@ -19,6 +19,7 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do before do build.run visit project_commit_path(project, project.commit.id) + wait_for_requests end it 'display icon with status' do @@ -26,7 +27,7 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do end it 'displays a mini pipeline graph' do - expect(page).to have_selector('[data-testid="pipeline-mini-graph"]') + expect(page).to have_selector('[data-testid="commit-box-mini-graph"]') first('.mini-pipeline-graph-dropdown-toggle').click diff --git a/spec/features/projects/environments/environment_metrics_spec.rb b/spec/features/projects/environments/environment_metrics_spec.rb index e8f197b67c2..0f858c627bc 100644 --- a/spec/features/projects/environments/environment_metrics_spec.rb +++ b/spec/features/projects/environments/environment_metrics_spec.rb @@ -27,12 +27,12 @@ RSpec.describe 'Environment > Metrics' do shared_examples 'has environment selector' do it 'has a working environment selector', :js do - click_link('See metrics') + click_link 'Monitoring' expect(page).to have_current_path(project_metrics_dashboard_path(project, environment: environment.id)) - expect(page).to have_css('[data-qa-selector="environments_dropdown"]') + expect(page).to have_css('[data-qa-selector="environments_dropdown"]') # rubocop:disable QA/SelectorUsage - within('[data-qa-selector="environments_dropdown"]') do + within('[data-qa-selector="environments_dropdown"]') do # rubocop:disable QA/SelectorUsage # Click on the dropdown click_on(environment.name) @@ -55,10 +55,10 @@ RSpec.describe 'Environment > Metrics' do create(:deployment, environment: environment, deployable: build) end - it 'shows metrics' do - click_link('See metrics') + it 'shows metrics', :js do + click_link 'Monitoring' - expect(page).to have_css('div#prometheus-graphs') + expect(page).to have_css('[data-qa-selector="prometheus_graphs"]') # rubocop:disable QA/SelectorUsage end it_behaves_like 'has environment selector' diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb index fea054de64e..5320f68b525 100644 --- a/spec/features/projects/environments/environment_spec.rb +++ b/spec/features/projects/environments/environment_spec.rb @@ -27,20 +27,6 @@ RSpec.describe 'Environment' do visit_environment(environment) end - it 'shows environment name' do - expect(page).to have_content(environment.name) - end - - context 'without auto-stop' do - it 'does not show auto-stop text' do - expect(page).not_to have_content('Auto stops') - end - - it 'does not show auto-stop button' do - expect(page).not_to have_selector(auto_stop_button_selector) - end - end - context 'with auto-stop' do let!(:environment) { create(:environment, :will_auto_stop, name: 'staging', project: project) } @@ -48,11 +34,11 @@ RSpec.describe 'Environment' do visit_environment(environment) end - it 'shows auto stop info' do + it 'shows auto stop info', :js do expect(page).to have_content('Auto stops') end - it 'shows auto stop button' do + it 'shows auto stop button', :js do expect(page).to have_selector(auto_stop_button_selector) expect(page.find(auto_stop_button_selector).find(:xpath, '..')['action']).to have_content(cancel_auto_stop_project_environment_path(environment.project, environment)) end @@ -80,7 +66,6 @@ RSpec.describe 'Environment' do it 'does show deployment SHA' do expect(page).to have_link(deployment.short_sha) expect(page).not_to have_link('Re-deploy') - expect(page).not_to have_terminal_button end end @@ -186,7 +171,7 @@ RSpec.describe 'Environment' do let(:build) { create(:ci_build, pipeline: pipeline) } let(:deployment) { create(:deployment, :success, environment: environment, deployable: build) } - it 'does show an external link button' do + it 'does show an external link button', :js do expect(page).to have_link(nil, href: environment.external_url) end end @@ -200,10 +185,6 @@ RSpec.describe 'Environment' do context 'for project maintainer' do let(:role) { :maintainer } - it 'shows the terminal button' do - expect(page).to have_terminal_button - end - context 'web terminal', :js do before do # Stub #terminals as it causes js-enabled feature specs to @@ -224,14 +205,6 @@ RSpec.describe 'Environment' do end end end - - context 'for developer' do - let(:role) { :developer } - - it 'does not show terminal button' do - expect(page).not_to have_terminal_button - end - end end end @@ -259,7 +232,7 @@ RSpec.describe 'Environment' do click_button('Stop') click_button('Stop environment') # confirm modal wait_for_all_requests - expect(page).to have_content('close_app') + expect(page).to have_button('Delete') end end @@ -269,7 +242,7 @@ RSpec.describe 'Environment' do name: action.ref, project: project) end - it 'does not allow to stop environment' do + it 'does not allow to stop environment', :js do expect(page).not_to have_button('Stop') end end @@ -277,7 +250,7 @@ RSpec.describe 'Environment' do context 'for reporter' do let(:role) { :reporter } - it 'does not show stop button' do + it 'does not show stop button', :js do expect(page).not_to have_button('Stop') end end @@ -287,7 +260,7 @@ RSpec.describe 'Environment' do context 'when environment is stopped' do let(:environment) { create(:environment, project: project, state: :stopped) } - it 'does not show stop button' do + it 'does not show stop button', :js do expect(page).not_to have_button('Stop') end end @@ -323,7 +296,7 @@ RSpec.describe 'Environment' do ref: 'feature') end - it 'user visits environment page' do + it 'user visits environment page', :js do visit_environment(environment) expect(page).to have_button('Stop') @@ -380,8 +353,4 @@ RSpec.describe 'Environment' do def visit_environment(environment) visit project_environment_path(environment.project, environment) end - - def have_terminal_button - have_link(nil, href: terminal_project_environment_path(project, environment)) - end end diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index 0dd4bd55d46..9413fae02e0 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -455,10 +455,10 @@ RSpec.describe 'Environments page', :js do expect(page).to have_content 'review-1' expect(page).to have_content 'review-2' within('.ci-table') do - within('[data-qa-selector="environment_item"]', text: 'review-1') do + within('[data-qa-selector="environment_item"]', text: 'review-1') do # rubocop:disable QA/SelectorUsage expect(find('.js-auto-stop').text).not_to be_empty end - within('[data-qa-selector="environment_item"]', text: 'review-2') do + within('[data-qa-selector="environment_item"]', text: 'review-2') do # rubocop:disable QA/SelectorUsage expect(find('.js-auto-stop').text).not_to be_empty end end diff --git a/spec/features/projects/environments_pod_logs_spec.rb b/spec/features/projects/environments_pod_logs_spec.rb index 5019e45593c..7d31de2b418 100644 --- a/spec/features/projects/environments_pod_logs_spec.rb +++ b/spec/features/projects/environments_pod_logs_spec.rb @@ -50,7 +50,7 @@ RSpec.describe 'Environment > Pod Logs', :js, :kubeclient do wait_for_requests - page.within('.qa-pods-dropdown') do + page.within('.qa-pods-dropdown') do # rubocop:disable QA/SelectorUsage find(".dropdown-toggle:not([disabled])").click dropdown_items = find(".dropdown-menu").all(".dropdown-item:not([disabled])") diff --git a/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb b/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb index 30bfcb645f4..221f07a2f75 100644 --- a/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb +++ b/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb @@ -91,7 +91,7 @@ RSpec.describe 'User sees feature flag list', :js do it 'shows the empty page' do expect(page).to have_text 'Get started with feature flags' expect(page).to have_selector('.btn-confirm', text: 'New feature flag') - expect(page).to have_selector('[data-qa-selector="configure_feature_flags_button"]', text: 'Configure') + expect(page).to have_selector('[data-qa-selector="configure_feature_flags_button"]', text: 'Configure') # rubocop:disable QA/SelectorUsage end end end diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb index 42f8daf9d5e..37583870cfd 100644 --- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb +++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb @@ -18,7 +18,7 @@ RSpec.describe 'Projects > Files > Project owner sees a link to create a license expect(current_path).to eq("/-/ide/project/#{project.full_path}/edit/master/-/LICENSE") - expect(page).to have_selector('.qa-file-templates-bar') + expect(page).to have_selector('.qa-file-templates-bar') # rubocop:disable QA/SelectorUsage select_template('MIT License') diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb index 9a6d1961a02..69e4303cce7 100644 --- a/spec/features/projects/fork_spec.rb +++ b/spec/features/projects/fork_spec.rb @@ -181,8 +181,8 @@ RSpec.describe 'Project fork' do it 'allows user to fork only to the group on fork page', :js do visit new_project_fork_path(project) - to_personal_namespace = find('[data-qa-selector=fork_namespace_button].disabled') - to_group = find(".fork-groups button[data-qa-name=#{group.name}]") + to_personal_namespace = find('[data-qa-selector=fork_namespace_button].disabled') # rubocop:disable QA/SelectorUsage + to_group = find(".fork-groups button[data-qa-name=#{group.name}]") # rubocop:disable QA/SelectorUsage expect(to_personal_namespace).not_to be_nil expect(to_group).not_to be_disabled diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb index 7f8ded4fa43..ccf3ccc6a96 100644 --- a/spec/features/projects/import_export/export_file_spec.rb +++ b/spec/features/projects/import_export/export_file_spec.rb @@ -82,8 +82,7 @@ RSpec.describe 'Import/Export - project export integration test', :js do relations << Gitlab::Json.parse(IO.read(project_json_path)) Dir.glob(File.join(tmpdir, 'tree/project', '*.ndjson')) do |rb_filename| File.foreach(rb_filename) do |line| - json = ActiveSupport::JSON.decode(line) - relations << json + relations << Gitlab::Json.parse(line) end end diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index a4c57e83bdd..302187917b7 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -62,6 +62,6 @@ RSpec.describe 'Import/Export - project import integration test', :js do end def click_import_project - find('[data-qa-panel-name="import_project"]').click + find('[data-qa-panel-name="import_project"]').click # rubocop:disable QA/SelectorUsage end end diff --git a/spec/features/projects/infrastructure_registry_spec.rb b/spec/features/projects/infrastructure_registry_spec.rb index c3cb3955092..16dd96e6c02 100644 --- a/spec/features/projects/infrastructure_registry_spec.rb +++ b/spec/features/projects/infrastructure_registry_spec.rb @@ -11,9 +11,9 @@ RSpec.describe 'Infrastructure Registry' do project.add_maintainer(user) end - context 'when feature is not available' do + context 'when packages registry is not enabled' do before do - stub_feature_flags(infrastructure_registry_page: false) + stub_config(packages: { enabled: false }) end it 'gives 404' do @@ -23,7 +23,7 @@ RSpec.describe 'Infrastructure Registry' do end end - context 'when feature is available', :js do + context 'when packages registry is enabled', :js do before do visit_project_infrastructure_registry end diff --git a/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb b/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb new file mode 100644 index 00000000000..f46cade9d5f --- /dev/null +++ b/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'User uses inherited settings', :js do + include JiraServiceHelper + + include_context 'project service activation' + + before do + stub_jira_integration_test + end + + shared_examples 'inherited settings' do + let_it_be(:project_settings) { { url: 'http://project.com', password: 'project' } } + + describe 'switching from inherited to custom settings' do + let_it_be(:integration) { create(:jira_integration, project: project, inherit_from_id: parent_integration.id, **project_settings) } + + it 'clears the form fields and saves the entered values' do + visit_project_integration('Jira') + + expect(page).not_to have_button('Use custom settings') + expect(page).to have_field('Web URL', with: parent_settings[:url], readonly: true) + expect(page).to have_field('Enter new password or API token', with: '', readonly: true) + + click_on 'Use default settings' + click_on 'Use custom settings' + + expect(page).not_to have_button('Use default settings') + expect(page).to have_field('Web URL', with: project_settings[:url], readonly: false) + expect(page).to have_field('Enter new password or API token', with: '', readonly: false) + + fill_in 'Web URL', with: 'http://custom.com' + fill_in 'Enter new password or API token', with: 'custom' + + click_save_integration + + expect(page).to have_text('Jira settings saved and active.') + expect(integration.reload).to have_attributes( + inherit_from_id: nil, + url: 'http://custom.com', + password: 'custom' + ) + end + end + + describe 'switching from custom to inherited settings' do + let_it_be(:integration) { create(:jira_integration, project: project, **project_settings) } + + it 'resets the form fields, makes them read-only, and saves the inherited values' do + visit_project_integration('Jira') + + expect(page).not_to have_button('Use default settings') + expect(page).to have_field('URL', with: project_settings[:url], readonly: false) + expect(page).to have_field('Enter new password or API token', with: '', readonly: false) + + click_on 'Use custom settings' + click_on 'Use default settings' + + expect(page).not_to have_button('Use custom settings') + expect(page).to have_field('URL', with: parent_settings[:url], readonly: true) + expect(page).to have_field('Enter new password or API token', with: '', readonly: true) + + click_save_integration + + expect(page).to have_text('Jira settings saved and active.') + expect(integration.reload).to have_attributes( + inherit_from_id: parent_integration.id, + **parent_settings + ) + end + end + end + + context 'with instance settings' do + let_it_be(:parent_settings) { { url: 'http://instance.com', password: 'instance' } } + let_it_be(:parent_integration) { create(:jira_integration, :instance, **parent_settings) } + + it_behaves_like 'inherited settings' + end + + context 'with group settings' do + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + let_it_be(:parent_settings) { { url: 'http://group.com', password: 'group' } } + let_it_be(:parent_integration) { create(:jira_integration, group: group, project: nil, **parent_settings) } + + it_behaves_like 'inherited settings' + end +end diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/invite_group_spec.rb index 6ce6834b5d5..8c3646125a5 100644 --- a/spec/features/projects/members/invite_group_spec.rb +++ b/spec/features/projects/members/invite_group_spec.rb @@ -13,7 +13,7 @@ RSpec.describe 'Project > Members > Invite group', :js do using RSpec::Parameterized::TableSyntax where(:invite_members_group_modal_enabled, :expected_invite_group_selector) do - true | 'button[data-qa-selector="invite_a_group_button"]' + true | 'button[data-qa-selector="invite_a_group_button"]' # rubocop:disable QA/SelectorUsage false | '#invite-group-tab' end @@ -43,7 +43,7 @@ RSpec.describe 'Project > Members > Invite group', :js do end describe 'Share with group lock' do - let(:invite_group_selector) { 'button[data-qa-selector="invite_a_group_button"]' } + let(:invite_group_selector) { 'button[data-qa-selector="invite_a_group_button"]' } # rubocop:disable QA/SelectorUsage shared_examples 'the project can be shared with groups' do it 'the "Invite a group" button exists' do diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index ef28979798f..0b293970703 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -6,374 +6,352 @@ RSpec.describe 'New project', :js do include Select2Helper include Spec::Support::Helpers::Features::TopNavSpecHelpers - shared_examples 'combined_menu: feature flag examples' do - context 'as a user' do - let(:user) { create(:user) } + context 'as a user' do + let(:user) { create(:user) } - before do - sign_in(user) - end + before do + sign_in(user) + end - it 'shows a message if multiple levels are restricted' do - Gitlab::CurrentSettings.update!( - restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL] - ) + it 'shows a message if multiple levels are restricted' do + Gitlab::CurrentSettings.update!( + restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL] + ) - visit new_project_path - find('[data-qa-panel-name="blank_project"]').click + visit new_project_path + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage - expect(page).to have_content 'Other visibility settings have been disabled by the administrator.' - end + expect(page).to have_content 'Other visibility settings have been disabled by the administrator.' + end - it 'shows a message if all levels are restricted' do - Gitlab::CurrentSettings.update!( - restricted_visibility_levels: Gitlab::VisibilityLevel.values - ) + it 'shows a message if all levels are restricted' do + Gitlab::CurrentSettings.update!( + restricted_visibility_levels: Gitlab::VisibilityLevel.values + ) - visit new_project_path - find('[data-qa-panel-name="blank_project"]').click + visit new_project_path + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage - expect(page).to have_content 'Visibility settings have been disabled by the administrator.' - end + expect(page).to have_content 'Visibility settings have been disabled by the administrator.' end + end - context 'as an admin' do - let(:user) { create(:admin) } + context 'as an admin' do + let(:user) { create(:admin) } - before do - sign_in(user) - end + before do + sign_in(user) + end - it 'shows "New project" page', :js do - visit new_project_path - find('[data-qa-panel-name="blank_project"]').click + it 'shows "New project" page', :js do + visit new_project_path + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage - expect(page).to have_content('Project name') - expect(page).to have_content('Project URL') - expect(page).to have_content('Project slug') + expect(page).to have_content('Project name') + expect(page).to have_content('Project URL') + expect(page).to have_content('Project slug') - click_link('New project') - find('[data-qa-panel-name="import_project"]').click + click_link('New project') + find('[data-qa-panel-name="import_project"]').click # rubocop:disable QA/SelectorUsage - expect(page).to have_link('GitHub') - expect(page).to have_link('Bitbucket') - expect(page).to have_link('GitLab.com') - expect(page).to have_button('Repo by URL') - expect(page).to have_link('GitLab export') - end + expect(page).to have_link('GitHub') + expect(page).to have_link('Bitbucket') + expect(page).to have_link('GitLab.com') + expect(page).to have_button('Repo by URL') + expect(page).to have_link('GitLab export') + end - describe 'manifest import option' do - before do - visit new_project_path + describe 'manifest import option' do + before do + visit new_project_path - find('[data-qa-panel-name="import_project"]').click - end + find('[data-qa-panel-name="import_project"]').click # rubocop:disable QA/SelectorUsage + end - it 'has Manifest file' do - expect(page).to have_link('Manifest file') - end + it 'has Manifest file' do + expect(page).to have_link('Manifest file') end + end - context 'Visibility level selector', :js do - Gitlab::VisibilityLevel.options.each do |key, level| - it "sets selector to #{key}" do - stub_application_setting(default_project_visibility: level) + context 'Visibility level selector', :js do + Gitlab::VisibilityLevel.options.each do |key, level| + it "sets selector to #{key}" do + stub_application_setting(default_project_visibility: level) - visit new_project_path - find('[data-qa-panel-name="blank_project"]').click - page.within('#blank-project-pane') do - expect(find_field("project_visibility_level_#{level}")).to be_checked - end + visit new_project_path + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage + page.within('#blank-project-pane') do + expect(find_field("project_visibility_level_#{level}")).to be_checked end + end - it "saves visibility level #{level} on validation error" do - visit new_project_path - find('[data-qa-panel-name="blank_project"]').click + it "saves visibility level #{level} on validation error" do + visit new_project_path + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage - choose(key) - click_button('Create project') - page.within('#blank-project-pane') do - expect(find_field("project_visibility_level_#{level}")).to be_checked - end + choose(key) + click_button('Create project') + page.within('#blank-project-pane') do + expect(find_field("project_visibility_level_#{level}")).to be_checked end end + end - context 'when group visibility is private but default is internal' do - let_it_be(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } + context 'when group visibility is private but default is internal' do + let_it_be(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } - before do - stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL) - end + before do + stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL) + end - context 'when admin mode is enabled', :enable_admin_mode do - it 'has private selected' do - visit new_project_path(namespace_id: group.id) - find('[data-qa-panel-name="blank_project"]').click + context 'when admin mode is enabled', :enable_admin_mode do + it 'has private selected' do + visit new_project_path(namespace_id: group.id) + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage - page.within('#blank-project-pane') do - expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked - end + page.within('#blank-project-pane') do + expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked end end + end - context 'when admin mode is disabled' do - it 'is not allowed' do - visit new_project_path(namespace_id: group.id) + context 'when admin mode is disabled' do + it 'is not allowed' do + visit new_project_path(namespace_id: group.id) - expect(page).to have_content('Not Found') - end + expect(page).to have_content('Not Found') end end + end - context 'when group visibility is public but user requests private' do - let_it_be(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + context 'when group visibility is public but user requests private' do + let_it_be(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } - before do - stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL) - end + before do + stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL) + end - context 'when admin mode is enabled', :enable_admin_mode do - it 'has private selected' do - visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE }) - find('[data-qa-panel-name="blank_project"]').click + context 'when admin mode is enabled', :enable_admin_mode do + it 'has private selected' do + visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE }) + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage - page.within('#blank-project-pane') do - expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked - end + page.within('#blank-project-pane') do + expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked end end + end - context 'when admin mode is disabled' do - it 'is not allowed' do - visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE }) + context 'when admin mode is disabled' do + it 'is not allowed' do + visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE }) - expect(page).to have_content('Not Found') - end + expect(page).to have_content('Not Found') end end end + end - context 'Readme selector' do - it 'shows the initialize with Readme checkbox on "Blank project" tab' do - visit new_project_path - find('[data-qa-panel-name="blank_project"]').click + context 'Readme selector' do + it 'shows the initialize with Readme checkbox on "Blank project" tab' do + visit new_project_path + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage - expect(page).to have_css('input#project_initialize_with_readme') - expect(page).to have_content('Initialize repository with a README') - end + expect(page).to have_css('input#project_initialize_with_readme') + expect(page).to have_content('Initialize repository with a README') + end - it 'does not show the initialize with Readme checkbox on "Create from template" tab' do - visit new_project_path - find('[data-qa-panel-name="create_from_template"]').click - first('.choose-template').click + it 'does not show the initialize with Readme checkbox on "Create from template" tab' do + visit new_project_path + find('[data-qa-panel-name="create_from_template"]').click # rubocop:disable QA/SelectorUsage + first('.choose-template').click - page.within '.project-fields-form' do - expect(page).not_to have_css('input#project_initialize_with_readme') - expect(page).not_to have_content('Initialize repository with a README') - end + page.within '.project-fields-form' do + expect(page).not_to have_css('input#project_initialize_with_readme') + expect(page).not_to have_content('Initialize repository with a README') end + end - it 'does not show the initialize with Readme checkbox on "Import project" tab' do - visit new_project_path - find('[data-qa-panel-name="import_project"]').click - first('.js-import-git-toggle-button').click + it 'does not show the initialize with Readme checkbox on "Import project" tab' do + visit new_project_path + find('[data-qa-panel-name="import_project"]').click # rubocop:disable QA/SelectorUsage + first('.js-import-git-toggle-button').click - page.within '#import-project-pane' do - expect(page).not_to have_css('input#project_initialize_with_readme') - expect(page).not_to have_content('Initialize repository with a README') - end + page.within '#import-project-pane' do + expect(page).not_to have_css('input#project_initialize_with_readme') + expect(page).not_to have_content('Initialize repository with a README') end end + end - context 'Namespace selector' do - context 'with user namespace' do - before do - visit new_project_path - find('[data-qa-panel-name="blank_project"]').click - end + context 'Namespace selector' do + context 'with user namespace' do + before do + visit new_project_path + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage + end - it 'selects the user namespace' do - page.within('#blank-project-pane') do - expect(page).to have_select('project[namespace_id]', visible: false, selected: user.username) - end + it 'selects the user namespace' do + page.within('#blank-project-pane') do + expect(page).to have_select('project[namespace_id]', visible: false, selected: user.username) end end + end - context 'with group namespace' do - let(:group) { create(:group, :private) } + context 'with group namespace' do + let(:group) { create(:group, :private) } - before do - group.add_owner(user) - visit new_project_path(namespace_id: group.id) - find('[data-qa-panel-name="blank_project"]').click - end + before do + group.add_owner(user) + visit new_project_path(namespace_id: group.id) + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage + end - it 'selects the group namespace' do - page.within('#blank-project-pane') do - expect(page).to have_select('project[namespace_id]', visible: false, selected: group.name) - end + it 'selects the group namespace' do + page.within('#blank-project-pane') do + expect(page).to have_select('project[namespace_id]', visible: false, selected: group.name) end end + end - context 'with subgroup namespace' do - let(:group) { create(:group) } - let(:subgroup) { create(:group, parent: group) } - - before do - group.add_maintainer(user) - visit new_project_path(namespace_id: subgroup.id) - find('[data-qa-panel-name="blank_project"]').click - end + context 'with subgroup namespace' do + let(:group) { create(:group) } + let(:subgroup) { create(:group, parent: group) } - it 'selects the group namespace' do - page.within('#blank-project-pane') do - expect(page).to have_select('project[namespace_id]', visible: false, selected: subgroup.full_path) - end - end + before do + group.add_maintainer(user) + visit new_project_path(namespace_id: subgroup.id) + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage end - context 'when changing namespaces dynamically', :js do - let(:public_group) { create(:group, :public) } - let(:internal_group) { create(:group, :internal) } - let(:private_group) { create(:group, :private) } - - before do - public_group.add_owner(user) - internal_group.add_owner(user) - private_group.add_owner(user) - visit new_project_path(namespace_id: public_group.id) - find('[data-qa-panel-name="blank_project"]').click - end - - it 'enables the correct visibility options' do - select2(user.namespace_id, from: '#project_namespace_id') - expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled - expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled - expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).not_to be_disabled - - select2(public_group.id, from: '#project_namespace_id') - expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled - expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled - expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).not_to be_disabled - - select2(internal_group.id, from: '#project_namespace_id') - expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled - expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled - expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).to be_disabled - - select2(private_group.id, from: '#project_namespace_id') - expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled - expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).to be_disabled - expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).to be_disabled + it 'selects the group namespace' do + page.within('#blank-project-pane') do + expect(page).to have_select('project[namespace_id]', visible: false, selected: subgroup.full_path) end end end - context 'Import project options', :js do + context 'when changing namespaces dynamically', :js do + let(:public_group) { create(:group, :public) } + let(:internal_group) { create(:group, :internal) } + let(:private_group) { create(:group, :private) } + before do - visit new_project_path - find('[data-qa-panel-name="import_project"]').click + public_group.add_owner(user) + internal_group.add_owner(user) + private_group.add_owner(user) + visit new_project_path(namespace_id: public_group.id) + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage end - context 'from git repository url, "Repo by URL"' do - before do - first('.js-import-git-toggle-button').click - end - - it 'does not autocomplete sensitive git repo URL' do - autocomplete = find('#project_import_url')['autocomplete'] - - expect(autocomplete).to eq('off') - end + it 'enables the correct visibility options' do + select2(user.namespace_id, from: '#project_namespace_id') + expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled + expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled + expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).not_to be_disabled + + select2(public_group.id, from: '#project_namespace_id') + expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled + expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled + expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).not_to be_disabled + + select2(internal_group.id, from: '#project_namespace_id') + expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled + expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled + expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).to be_disabled + + select2(private_group.id, from: '#project_namespace_id') + expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled + expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).to be_disabled + expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).to be_disabled + end + end + end - it 'shows import instructions' do - git_import_instructions = first('.js-toggle-content') + context 'Import project options', :js do + before do + visit new_project_path + find('[data-qa-panel-name="import_project"]').click # rubocop:disable QA/SelectorUsage + end - expect(git_import_instructions).to be_visible - expect(git_import_instructions).to have_content 'Git repository URL' - end + context 'from git repository url, "Repo by URL"' do + before do + first('.js-import-git-toggle-button').click + end - it 'reports error if repo URL does not end with .git' do - fill_in 'project_import_url', with: 'http://foo/bar' - # simulate blur event - find('body').click + it 'does not autocomplete sensitive git repo URL' do + autocomplete = find('#project_import_url')['autocomplete'] - expect(page).to have_text('A repository URL usually ends in a .git suffix') - end + expect(autocomplete).to eq('off') + end - it 'keeps "Import project" tab open after form validation error' do - collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace) + it 'shows import instructions' do + git_import_instructions = first('.js-toggle-content') - fill_in 'project_import_url', with: collision_project.http_url_to_repo - fill_in 'project_name', with: collision_project.name + expect(git_import_instructions).to be_visible + expect(git_import_instructions).to have_content 'Git repository URL' + end - click_on 'Create project' + it 'reports error if repo URL does not end with .git' do + fill_in 'project_import_url', with: 'http://foo/bar' + # simulate blur event + find('body').click - expect(page).to have_css('#import-project-pane.active') - expect(page).not_to have_css('.toggle-import-form.hide') - end + expect(page).to have_text('A repository URL usually ends in a .git suffix') end - context 'from GitHub' do - before do - first('.js-import-github').click - end + it 'keeps "Import project" tab open after form validation error' do + collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace) - it 'shows import instructions' do - expect(page).to have_content('Authenticate with GitHub') - expect(current_path).to eq new_import_github_path - end - end + fill_in 'project_import_url', with: collision_project.http_url_to_repo + fill_in 'project_name', with: collision_project.name - context 'from manifest file' do - before do - first('.import_manifest').click - end + click_on 'Create project' - it 'shows import instructions' do - expect(page).to have_content('Manifest file import') - expect(current_path).to eq new_import_manifest_path - end + expect(page).to have_css('#import-project-pane.active') + expect(page).not_to have_css('.toggle-import-form.hide') end end - context 'Namespace selector' do - context 'with group with DEVELOPER_MAINTAINER_PROJECT_ACCESS project_creation_level' do - let(:group) { create(:group, project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) } - - before do - group.add_developer(user) - visit new_project_path(namespace_id: group.id) - find('[data-qa-panel-name="blank_project"]').click - end + context 'from GitHub' do + before do + first('.js-import-github').click + end - it 'selects the group namespace' do - page.within('#blank-project-pane') do - expect(page).to have_select('project[namespace_id]', visible: false, selected: group.full_path) - end - end + it 'shows import instructions' do + expect(page).to have_content('Authenticate with GitHub') + expect(current_path).to eq new_import_github_path end end - end - end - context 'with combined_menu feature flag on' do - let(:needs_rewrite_for_combined_menu_flag_on) { true } + context 'from manifest file' do + before do + first('.import_manifest').click + end - before do - stub_feature_flags(combined_menu: true) + it 'shows import instructions' do + expect(page).to have_content('Manifest file import') + expect(current_path).to eq new_import_manifest_path + end + end end - it_behaves_like 'combined_menu: feature flag examples' - end + context 'Namespace selector' do + context 'with group with DEVELOPER_MAINTAINER_PROJECT_ACCESS project_creation_level' do + let(:group) { create(:group, project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) } - context 'with combined_menu feature flag off' do - let(:needs_rewrite_for_combined_menu_flag_on) { false } + before do + group.add_developer(user) + visit new_project_path(namespace_id: group.id) + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage + end - before do - stub_feature_flags(combined_menu: false) + it 'selects the group namespace' do + page.within('#blank-project-pane') do + expect(page).to have_select('project[namespace_id]', visible: false, selected: group.full_path) + end + end + end end - - it_behaves_like 'combined_menu: feature flag examples' end end diff --git a/spec/features/projects/packages_spec.rb b/spec/features/projects/packages_spec.rb index fa4c57c305d..30298f79312 100644 --- a/spec/features/projects/packages_spec.rb +++ b/spec/features/projects/packages_spec.rb @@ -45,6 +45,8 @@ RSpec.describe 'Packages' do it_behaves_like 'package details link' end + it_behaves_like 'package details link' + context 'deleting a package' do let_it_be(:project) { create(:project) } let_it_be(:package) { create(:package, project: project) } diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index ce2083b397a..944cee2a998 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -240,10 +240,14 @@ RSpec.describe 'Pipeline', :js do end end - it 'is possible to retry the success job' do + it 'is possible to retry the success job', :sidekiq_might_not_need_inline do find('#ci-badge-build .ci-action-icon-container').click + wait_for_requests expect(page).not_to have_content('Retry job') + within('.js-pipeline-header-container') do + expect(page).to have_selector('.js-ci-status-icon-running') + end end end @@ -282,10 +286,14 @@ RSpec.describe 'Pipeline', :js do end end - it 'is possible to retry the failed build' do + it 'is possible to retry the failed build', :sidekiq_might_not_need_inline do find('#ci-badge-test .ci-action-icon-container').click + wait_for_requests expect(page).not_to have_content('Retry job') + within('.js-pipeline-header-container') do + expect(page).to have_selector('.js-ci-status-icon-running') + end end it 'includes the failure reason' do @@ -308,10 +316,14 @@ RSpec.describe 'Pipeline', :js do end end - it 'is possible to play the manual job' do + it 'is possible to play the manual job', :sidekiq_might_not_need_inline do find('#ci-badge-manual-build .ci-action-icon-container').click + wait_for_requests expect(page).not_to have_content('Play job') + within('.js-pipeline-header-container') do + expect(page).to have_selector('.js-ci-status-icon-running') + end end end @@ -411,11 +423,18 @@ RSpec.describe 'Pipeline', :js do context 'when retrying' do before do find('[data-testid="retryPipeline"]').click + wait_for_requests end it 'does not show a "Retry" button', :sidekiq_might_not_need_inline do expect(page).not_to have_content('Retry') end + + it 'shows running status in pipeline header', :sidekiq_might_not_need_inline do + within('.js-pipeline-header-container') do + expect(page).to have_selector('.js-ci-status-icon-running') + end + end end end @@ -437,44 +456,28 @@ RSpec.describe 'Pipeline', :js do end end - shared_context 'delete pipeline' do - context 'deleting pipeline' do - context 'when user can not delete' do - before do - visit_pipeline - end - - it { expect(page).not_to have_button('Delete') } - end - - context 'when deleting' do - before do - group.add_owner(user) - - visit_pipeline - - click_button 'Delete' - click_button 'Delete pipeline' - end - - it 'redirects to pipeline overview page', :sidekiq_inline do - expect(page).to have_content('The pipeline has been deleted') - expect(current_path).to eq(project_pipelines_path(project)) - end - end + context 'when user can not delete' do + before do + visit_pipeline end - end - context 'when cancel_pipelines_prior_to_destroy is enabled' do - include_context 'delete pipeline' + it { expect(page).not_to have_button('Delete') } end - context 'when cancel_pipelines_prior_to_destroy is disabled' do + context 'when deleting' do before do - stub_feature_flags(cancel_pipelines_prior_to_destroy: false) + group.add_owner(user) + + visit_pipeline + + click_button 'Delete' + click_button 'Delete pipeline' end - include_context 'delete pipeline' + it 'redirects to pipeline overview page', :sidekiq_inline do + expect(page).to have_content('The pipeline has been deleted') + expect(current_path).to eq(project_pipelines_path(project)) + end end context 'when pipeline ref does not exist in repository anymore' do @@ -775,65 +778,10 @@ RSpec.describe 'Pipeline', :js do describe 'GET /:project/-/pipelines/:id' do subject { visit project_pipeline_path(project, pipeline) } - # remove when :graphql_pipeline_details flag is removed - # https://gitlab.com/gitlab-org/gitlab/-/issues/299112 - context 'when :graphql_pipeline_details flag is off' do - before do - stub_feature_flags(graphql_pipeline_details: false) - stub_feature_flags(graphql_pipeline_details_users: false) - end - - it 'shows deploy job as created' do - subject - - within('.pipeline-header-container') do - expect(page).to have_content('pending') - end - - within('.js-pipeline-graph') do - within '.stage-column:nth-child(1)' do - expect(page).to have_content('test') - expect(page).to have_css('.ci-status-icon-pending') - end - - within '.stage-column:nth-child(2)' do - expect(page).to have_content('deploy') - expect(page).to have_css('.ci-status-icon-created') - end - end - end - - context 'when test job succeeded' do - before do - test_job.success! - end - - it 'shows deploy job as pending' do - subject - - within('.pipeline-header-container') do - expect(page).to have_content('running') - end - - within('.pipeline-graph') do - within '.stage-column:nth-child(1)' do - expect(page).to have_content('test') - expect(page).to have_css('.ci-status-icon-success') - end - - within '.stage-column:nth-child(2)' do - expect(page).to have_content('deploy') - expect(page).to have_css('.ci-status-icon-pending') - end - end - end - end - end - it 'shows deploy job as created' do subject - within('.pipeline-header-container') do + within('.js-pipeline-header-container') do expect(page).to have_content('pending') end @@ -858,7 +806,7 @@ RSpec.describe 'Pipeline', :js do it 'shows deploy job as pending' do subject - within('.pipeline-header-container') do + within('.js-pipeline-header-container') do expect(page).to have_content('running') end @@ -887,7 +835,7 @@ RSpec.describe 'Pipeline', :js do it 'shows deploy job as waiting for resource' do subject - within('.pipeline-header-container') do + within('.js-pipeline-header-container') do expect(page).to have_content('waiting') end @@ -899,29 +847,6 @@ RSpec.describe 'Pipeline', :js do end end - # remove when :graphql_pipeline_details flag is removed - # https://gitlab.com/gitlab-org/gitlab/-/issues/299112 - context 'when :graphql_pipeline_details flag is off' do - before do - stub_feature_flags(graphql_pipeline_details: false) - stub_feature_flags(graphql_pipeline_details_users: false) - end - it 'shows deploy job as waiting for resource' do - subject - - within('.pipeline-header-container') do - expect(page).to have_content('waiting') - end - - within('.pipeline-graph') do - within '.stage-column:nth-child(2)' do - expect(page).to have_content('deploy') - expect(page).to have_css('.ci-status-icon-waiting-for-resource') - end - end - end - end - context 'when resource is released from another job' do before do another_job.success! @@ -930,7 +855,7 @@ RSpec.describe 'Pipeline', :js do it 'shows deploy job as pending' do subject - within('.pipeline-header-container') do + within('.js-pipeline-header-container') do expect(page).to have_content('running') end @@ -941,29 +866,6 @@ RSpec.describe 'Pipeline', :js do end end end - - # remove when :graphql_pipeline_details flag is removed - # https://gitlab.com/gitlab-org/gitlab/-/issues/299112 - context 'when :graphql_pipeline_details flag is off' do - before do - stub_feature_flags(graphql_pipeline_details: false) - stub_feature_flags(graphql_pipeline_details_users: false) - end - it 'shows deploy job as pending' do - subject - - within('.pipeline-header-container') do - expect(page).to have_content('running') - end - - within('.pipeline-graph') do - within '.stage-column:nth-child(2)' do - expect(page).to have_content('deploy') - expect(page).to have_css('.ci-status-icon-pending') - end - end - end - end end context 'when deploy job is a bridge to trigger a downstream pipeline' do @@ -975,7 +877,7 @@ RSpec.describe 'Pipeline', :js do it 'shows deploy job as waiting for resource' do subject - within('.pipeline-header-container') do + within('.js-pipeline-header-container') do expect(page).to have_content('waiting') end @@ -997,7 +899,7 @@ RSpec.describe 'Pipeline', :js do it 'shows deploy job as waiting for resource' do subject - within('.pipeline-header-container') do + within('.js-pipeline-header-container') do expect(page).to have_content('waiting') end @@ -1231,23 +1133,6 @@ RSpec.describe 'Pipeline', :js do expect(page).not_to have_content('Failed Jobs') expect(page).to have_selector('.js-pipeline-graph') end - - # remove when :graphql_pipeline_details flag is removed - # https://gitlab.com/gitlab-org/gitlab/-/issues/299112 - context 'when :graphql_pipeline_details flag is off' do - before do - stub_feature_flags(graphql_pipeline_details: false) - stub_feature_flags(graphql_pipeline_details_users: false) - end - - it 'displays the pipeline graph' do - subject - - expect(current_path).to eq(pipeline_path(pipeline)) - expect(page).not_to have_content('Failed Jobs') - expect(page).to have_selector('.pipeline-visualization') - end - end end end diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 1de0eea4657..bd22c8632e4 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -12,8 +12,6 @@ RSpec.describe 'Pipelines', :js do before do sign_in(user) - stub_feature_flags(graphql_pipeline_details: false) - stub_feature_flags(graphql_pipeline_details_users: false) project.add_developer(user) project.update!(auto_devops_attributes: { enabled: false }) @@ -585,6 +583,26 @@ RSpec.describe 'Pipelines', :js do expect(page).to have_selector('.gl-pagination .page-link', count: 4) end end + + context 'with pipeline key selection' do + before do + visit project_pipelines_path(project) + wait_for_requests + end + + it 'changes the Pipeline ID column for Pipeline IID' do + page.find('[data-testid="pipeline-key-dropdown"]').click + + within '.gl-new-dropdown-contents' do + dropdown_options = page.find_all '.gl-new-dropdown-item' + + dropdown_options[1].click + end + + expect(page.find('[data-testid="pipeline-th"]')).to have_content 'Pipeline IID' + expect(page.find('[data-testid="pipeline-url-link"]')).to have_content "##{pipeline.iid}" + end + end end describe 'GET /:project/-/pipelines/show' do diff --git a/spec/features/projects/services/user_activates_irker_spec.rb b/spec/features/projects/services/user_activates_irker_spec.rb index e4d92dc30ff..004aa116bb3 100644 --- a/spec/features/projects/services/user_activates_irker_spec.rb +++ b/spec/features/projects/services/user_activates_irker_spec.rb @@ -2,16 +2,16 @@ require 'spec_helper' -RSpec.describe 'User activates Irker (IRC gateway)' do +RSpec.describe 'User activates irker (IRC gateway)' do include_context 'project service activation' it 'activates service', :js do - visit_project_integration('Irker (IRC gateway)') + visit_project_integration('irker (IRC gateway)') check('Colorize messages') fill_in('Recipients', with: 'irc://chat.freenode.net/#commits') click_test_then_save_integration(expect_test_to_fail: false) - expect(page).to have_content('Irker (IRC gateway) settings saved and active.') + expect(page).to have_content('irker (IRC gateway) settings saved and active.') end end diff --git a/spec/features/projects/services/user_activates_pushover_spec.rb b/spec/features/projects/services/user_activates_pushover_spec.rb index 97003ab7c2a..d92f69e700a 100644 --- a/spec/features/projects/services/user_activates_pushover_spec.rb +++ b/spec/features/projects/services/user_activates_pushover_spec.rb @@ -14,7 +14,7 @@ RSpec.describe 'User activates Pushover' do fill_in('API key', with: 'verySecret') fill_in('User key', with: 'verySecret') fill_in('Device', with: 'myDevice') - select('High Priority', from: 'Priority') + select('High priority', from: 'Priority') select('Bike', from: 'Sound') click_test_then_save_integration(expect_test_to_fail: false) diff --git a/spec/features/projects/services/user_views_services_spec.rb b/spec/features/projects/services/user_views_services_spec.rb index b936a7f38f6..201a58ba379 100644 --- a/spec/features/projects/services/user_views_services_spec.rb +++ b/spec/features/projects/services/user_views_services_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'User views services', :js do expect(page).to have_content('Atlassian Bamboo') expect(page).to have_content('JetBrains TeamCity') expect(page).to have_content('Asana') - expect(page).to have_content('Irker (IRC gateway)') + expect(page).to have_content('irker (IRC gateway)') expect(page).to have_content('Packagist') end end diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb index 3f9f2dae453..509729d526d 100644 --- a/spec/features/projects/settings/registry_settings_spec.rb +++ b/spec/features/projects/settings/registry_settings_spec.rb @@ -9,12 +9,12 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p let_it_be(:project, reload: true) { create(:project, namespace: user.namespace) } let(:container_registry_enabled) { true } - let(:container_registry_enabled_on_project) { true } + let(:container_registry_enabled_on_project) { ProjectFeature::ENABLED } subject { visit project_settings_packages_and_registries_path(project) } before do - project.update!(container_registry_enabled: container_registry_enabled_on_project) + project.project_feature.update!(container_registry_access_level: container_registry_enabled_on_project) project.container_expiration_policy.update!(enabled: true) sign_in(user) @@ -104,7 +104,7 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p end context 'when container registry is disabled on project' do - let(:container_registry_enabled_on_project) { false } + let(:container_registry_enabled_on_project) { ProjectFeature::DISABLED } it 'does not exists' do subject diff --git a/spec/features/projects/show/schema_markup_spec.rb b/spec/features/projects/show/schema_markup_spec.rb index 28803db924a..8adbdb64f1b 100644 --- a/spec/features/projects/show/schema_markup_spec.rb +++ b/spec/features/projects/show/schema_markup_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'Projects > Show > Schema Markup' do expect(page).to have_selector('[itemprop="identifier"]', text: "Project ID: #{project.id}") expect(page).to have_selector('[itemprop="description"]', text: project.description) expect(page).to have_selector('[itemprop="license"]', text: project.repository.license.name) - expect(find_all('[itemprop="keywords"]').map(&:text)).to match_array(project.topic_list.map(&:capitalize)) + expect(find_all('[itemprop="keywords"]').map(&:text)).to match_array(project.topic_list) expect(page).to have_selector('[itemprop="about"]') end end diff --git a/spec/features/projects/show/user_sees_collaboration_links_spec.rb b/spec/features/projects/show/user_sees_collaboration_links_spec.rb index 613033373e8..552f068ecc7 100644 --- a/spec/features/projects/show/user_sees_collaboration_links_spec.rb +++ b/spec/features/projects/show/user_sees_collaboration_links_spec.rb @@ -39,7 +39,7 @@ RSpec.describe 'Projects > Show > Collaboration links', :js do # The dropdown above the tree page.within('.repo-breadcrumb') do - find('.qa-add-to-tree').click + find('.qa-add-to-tree').click # rubocop:disable QA/SelectorUsage aggregate_failures 'dropdown links above the repo tree' do expect(page).to have_link('New file') @@ -71,7 +71,7 @@ RSpec.describe 'Projects > Show > Collaboration links', :js do find_new_menu_toggle.click end - expect(page).not_to have_selector('.qa-add-to-tree') + expect(page).not_to have_selector('.qa-add-to-tree') # rubocop:disable QA/SelectorUsage expect(page).not_to have_link('Web IDE') end diff --git a/spec/features/projects/tags/user_edits_tags_spec.rb b/spec/features/projects/tags/user_edits_tags_spec.rb index 7a8a685f3d9..9f66b7274e8 100644 --- a/spec/features/projects/tags/user_edits_tags_spec.rb +++ b/spec/features/projects/tags/user_edits_tags_spec.rb @@ -55,7 +55,7 @@ RSpec.describe 'Project > Tags', :js do note_textarea = page.find('.js-gfm-input') # Click on Bold button - page.find('.md-header-toolbar button.toolbar-btn:first-child').click + page.find('.md-header-toolbar button:first-child').click expect(note_textarea.value).to eq('****') end diff --git a/spec/features/projects/terraform_spec.rb b/spec/features/projects/terraform_spec.rb index d080d101285..2c63f2bfc02 100644 --- a/spec/features/projects/terraform_spec.rb +++ b/spec/features/projects/terraform_spec.rb @@ -38,7 +38,7 @@ RSpec.describe 'Terraform', :js do it 'displays a table with terraform states' do expect(page).to have_selector( - '[data-testid="terraform-states-table-name"]', + "[data-testid='terraform-states-table-name']", count: project.terraform_states.size ) end @@ -64,7 +64,7 @@ RSpec.describe 'Terraform', :js do expect(page).to have_content(additional_state.name) find("[data-testid='terraform-state-actions-#{additional_state.name}']").click - find('[data-testid="terraform-state-remove"]').click + find("[data-testid='terraform-state-remove']").click fill_in "terraform-state-remove-input-#{additional_state.name}", with: additional_state.name click_button 'Remove' @@ -72,6 +72,21 @@ RSpec.describe 'Terraform', :js do expect { additional_state.reload }.to raise_error ActiveRecord::RecordNotFound end end + + context 'when clicking on copy Terraform init command' do + it 'shows the modal with the init command' do + visit project_terraform_index_path(project) + + expect(page).to have_content(terraform_state.name) + + page.within("[data-testid='terraform-state-actions-#{terraform_state.name}']") do + click_button class: 'gl-dropdown-toggle' + click_button 'Copy Terraform init command' + end + + expect(page).to have_content("To get access to this terraform state from your local computer, run the following command at the command line.") + end + end end end @@ -87,11 +102,11 @@ RSpec.describe 'Terraform', :js do context 'when user visits the index page' do it 'displays a table without an action dropdown', :aggregate_failures do expect(page).to have_selector( - '[data-testid="terraform-states-table-name"]', + "[data-testid='terraform-states-table-name']", count: project.terraform_states.size ) - expect(page).not_to have_selector('[data-testid*="terraform-state-actions"]') + expect(page).not_to have_selector("[data-testid*='terraform-state-actions']") end end end diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb index e2ae858cb9b..f6127b38bd6 100644 --- a/spec/features/projects/tree/create_directory_spec.rb +++ b/spec/features/projects/tree/create_directory_spec.rb @@ -49,8 +49,8 @@ RSpec.describe 'Multi-file editor new directory', :js do # Compact mode depends on the size of window. If it is shorter than MAX_WINDOW_HEIGHT_COMPACT, # (as it is with WEBDRIVER_HEADLESS=0), this initial commit button will exist. Otherwise, if it is # taller (as it is by default with chrome headless) then the button will not exist. - if page.has_css?('.qa-begin-commit-button') - find('.qa-begin-commit-button').click + if page.has_css?('.qa-begin-commit-button') # rubocop:disable QA/SelectorUsage + find('.qa-begin-commit-button').click # rubocop:disable QA/SelectorUsage end fill_in('commit-message', with: 'commit message ide') diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb index 956b8898854..33be02a9121 100644 --- a/spec/features/projects/tree/create_file_spec.rb +++ b/spec/features/projects/tree/create_file_spec.rb @@ -39,8 +39,8 @@ RSpec.describe 'Multi-file editor new file', :js do # Compact mode depends on the size of window. If it is shorter than MAX_WINDOW_HEIGHT_COMPACT, # (as it is with WEBDRIVER_HEADLESS=0), this initial commit button will exist. Otherwise, if it is # taller (as it is by default with chrome headless) then the button will not exist. - if page.has_css?('.qa-begin-commit-button') - find('.qa-begin-commit-button').click + if page.has_css?('.qa-begin-commit-button') # rubocop:disable QA/SelectorUsage + find('.qa-begin-commit-button').click # rubocop:disable QA/SelectorUsage end fill_in('commit-message', with: 'commit message ide') diff --git a/spec/features/projects/tree/tree_show_spec.rb b/spec/features/projects/tree/tree_show_spec.rb index ca9e0a23888..f8bbaa9535b 100644 --- a/spec/features/projects/tree/tree_show_spec.rb +++ b/spec/features/projects/tree/tree_show_spec.rb @@ -26,7 +26,7 @@ RSpec.describe 'Projects tree', :js do expect(page).to have_selector('.tree-item') expect(page).to have_content('add tests for .gitattributes custom highlighting') expect(page).not_to have_selector('.flash-alert') - expect(page).not_to have_selector('[data-qa-selector="label-lfs"]', text: 'LFS') + expect(page).not_to have_selector('[data-qa-selector="label-lfs"]', text: 'LFS') # rubocop:disable QA/SelectorUsage end it 'renders tree table for a subtree without errors' do @@ -35,7 +35,7 @@ RSpec.describe 'Projects tree', :js do expect(page).to have_selector('.tree-item') expect(page).to have_content('add spaces in whitespace file') - expect(page).not_to have_selector('[data-qa-selector="label-lfs"]', text: 'LFS') + expect(page).not_to have_selector('[data-qa-selector="label-lfs"]', text: 'LFS') # rubocop:disable QA/SelectorUsage expect(page).not_to have_selector('.flash-alert') end @@ -112,7 +112,7 @@ RSpec.describe 'Projects tree', :js do it 'renders LFS badge on blob item' do visit project_tree_path(project, File.join('master', 'files/lfs')) - expect(page).to have_selector('[data-qa-selector="label-lfs"]', text: 'LFS') + expect(page).to have_selector('[data-qa-selector="label-lfs"]', text: 'LFS') # rubocop:disable QA/SelectorUsage end end diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb index a5b51bac747..2dc2f168896 100644 --- a/spec/features/projects/user_creates_project_spec.rb +++ b/spec/features/projects/user_creates_project_spec.rb @@ -8,17 +8,14 @@ RSpec.describe 'User creates a project', :js do before do sign_in(user) create(:personal_key, user: user) - - stub_experiments(new_project_readme: :candidate) end it 'creates a new project' do visit(new_project_path) - find('[data-qa-panel-name="blank_project"]').click + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage fill_in(:project_name, with: 'Empty') - # part of the new_project_readme experiment expect(page).to have_checked_field 'Initialize repository with a README' uncheck 'Initialize repository with a README' @@ -46,7 +43,7 @@ RSpec.describe 'User creates a project', :js do it 'creates a new project' do visit(new_project_path) - find('[data-qa-panel-name="blank_project"]').click + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage fill_in :project_name, with: 'A Subgroup Project' fill_in :project_path, with: 'a-subgroup-project' @@ -75,7 +72,7 @@ RSpec.describe 'User creates a project', :js do it 'creates a new project' do visit(new_project_path) - find('[data-qa-panel-name="blank_project"]').click + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage fill_in :project_name, with: 'a-new-project' fill_in :project_path, with: 'a-new-project' diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index a3d134d49eb..59ad7d31ea7 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'Project' do shared_examples 'creates from template' do |template, sub_template_tab = nil| it "is created from template", :js do - find('[data-qa-panel-name="create_from_template"]').click + find('[data-qa-panel-name="create_from_template"]').click # rubocop:disable QA/SelectorUsage find(".project-template #{sub_template_tab}").click if sub_template_tab find("label[for=#{template.name}]").click fill_in("project_name", with: template.name) @@ -132,8 +132,8 @@ RSpec.describe 'Project' do visit path - expect(page).to have_css('.home-panel-topic-list') - expect(page).to have_link('Topic1', href: explore_projects_path(topic: 'topic1')) + expect(page).to have_selector('[data-testid="project_topic_list"]') + expect(page).to have_link('topic1', href: explore_projects_path(topic: 'topic1')) end it 'shows up to 3 project topics' do @@ -141,10 +141,10 @@ RSpec.describe 'Project' do visit path - expect(page).to have_css('.home-panel-topic-list') - expect(page).to have_link('Topic1', href: explore_projects_path(topic: 'topic1')) - expect(page).to have_link('Topic2', href: explore_projects_path(topic: 'topic2')) - expect(page).to have_link('Topic3', href: explore_projects_path(topic: 'topic3')) + expect(page).to have_selector('[data-testid="project_topic_list"]') + expect(page).to have_link('topic1', href: explore_projects_path(topic: 'topic1')) + expect(page).to have_link('topic2', href: explore_projects_path(topic: 'topic2')) + expect(page).to have_link('topic3', href: explore_projects_path(topic: 'topic3')) expect(page).to have_content('+ 1 more') end end @@ -290,7 +290,7 @@ RSpec.describe 'Project' do it 'has working links to submodules' do click_link('645f6c4c') - expect(page).to have_selector('.qa-branches-select', text: '645f6c4c82fd3f5e06f67134450a570b795e55a6') + expect(page).to have_selector('.qa-branches-select', text: '645f6c4c82fd3f5e06f67134450a570b795e55a6') # rubocop:disable QA/SelectorUsage end context 'for signed commit on default branch', :js do diff --git a/spec/features/registrations/welcome_spec.rb b/spec/features/registrations/welcome_spec.rb deleted file mode 100644 index 74320b69f19..00000000000 --- a/spec/features/registrations/welcome_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -# 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 b61a769185e..22de77f7cd0 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -16,10 +16,10 @@ RSpec.describe 'Runners' do project.add_maintainer(user) end - it 'user can see a button to install runners on kubernetes clusters' do + it 'user can see a link with instructions on how to install GitLab Runner' do visit project_runners_path(project) - expect(page).to have_link('Install GitLab Runner on Kubernetes', href: project_clusters_path(project)) + expect(page).to have_link('Install GitLab Runner and ensure it\'s running.', href: "https://docs.gitlab.com/runner/install/") end end @@ -343,12 +343,6 @@ RSpec.describe 'Runners' do expect(page).to have_content 'No runners found' end - - it 'user can see a link to install runners on kubernetes clusters' do - visit group_settings_ci_cd_path(group) - - expect(page).to have_link('Install GitLab Runner on Kubernetes', href: group_clusters_path(group)) - end end context 'group with a runner' 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 c002d199b01..8736f16b991 100644 --- a/spec/features/search/user_uses_header_search_field_spec.rb +++ b/spec/features/search/user_uses_header_search_field_spec.rb @@ -254,6 +254,7 @@ RSpec.describe 'User uses header search field', :js do href = search_path(search: term) href.concat("&project_id=#{project_id}") if project_id href.concat("&group_id=#{group_id}") if group_id + href.concat("&nav_source=navbar") ".dropdown a[href='#{href}']" end diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb index 0309df8f32a..fb2873f1c96 100644 --- a/spec/features/users/show_spec.rb +++ b/spec/features/users/show_spec.rb @@ -57,6 +57,56 @@ RSpec.describe 'User page' do end end + context 'location' do + let_it_be(:location) { 'San Francisco, CA' } + + context 'when location is set' do + let_it_be(:user) { create(:user, location: location) } + + it 'shows location' do + subject + + expect(page).to have_content(location) + end + end + + context 'when location is not set' do + it 'does not show location' do + subject + + expect(page).not_to have_content(location) + end + end + end + + context 'timezone' do + let_it_be(:timezone) { 'America/Los_Angeles' } + + before do + travel_to Time.find_zone(timezone).local(2021, 7, 20, 15, 30, 45) + end + + context 'when timezone is set' do + let_it_be(:user) { create(:user, timezone: timezone) } + + it 'shows local time' do + subject + + expect(page).to have_content('3:30 PM') + end + end + + context 'when timezone is invalid' do + let_it_be(:user) { create(:user, timezone: 'Foo/Bar') } + + it 'shows local time using the configured default timezone (UTC in this case)' do + subject + + expect(page).to have_content('10:30 PM') + end + end + end + context 'follow/unfollow and followers/following' do let_it_be(:followee) { create(:user) } let_it_be(:follower) { create(:user) } @@ -228,6 +278,14 @@ RSpec.describe 'User page' do expect(page).to have_content("(they/them)") end + it 'shows the pronunctiation of the user if there was one' do + user.user_detail.update_column(:pronunciation, 'pruh-nuhn-see-ay-shn') + + subject + + expect(page).to have_content("Pronounced as: pruh-nuhn-see-ay-shn") + end + context 'signup disabled' do it 'shows the sign in link' do stub_application_setting(signup_enabled: false) |