diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-18 11:17:02 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-18 11:17:02 +0300 |
commit | b39512ed755239198a9c294b6a45e65c05900235 (patch) | |
tree | d234a3efade1de67c46b9e5a38ce813627726aa7 /spec/features | |
parent | d31474cf3b17ece37939d20082b07f6657cc79a9 (diff) |
Add latest changes from gitlab-org/gitlab@15-3-stable-eev15.3.0-rc42
Diffstat (limited to 'spec/features')
108 files changed, 1532 insertions, 1253 deletions
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index 8bf8ef56353..b297d92b2fa 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -6,161 +6,168 @@ RSpec.describe 'Admin Appearance' do let!(:appearance) { create(:appearance) } let(:admin) { create(:admin) } - it 'create new appearance' do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - visit admin_application_settings_appearances_path - - fill_in 'appearance_title', with: 'MyCompany' - fill_in 'appearance_description', with: 'dev server' - fill_in 'appearance_new_project_guidelines', with: 'Custom project guidelines' - fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines' - click_button 'Update appearance settings' - - expect(page).to have_current_path admin_application_settings_appearances_path, ignore_query: true - expect(page).to have_content 'Appearance' - - expect(page).to have_field('appearance_title', with: 'MyCompany') - expect(page).to have_field('appearance_description', with: 'dev server') - expect(page).to have_field('appearance_new_project_guidelines', with: 'Custom project guidelines') - expect(page).to have_field('appearance_profile_image_guidelines', with: 'Custom profile image guidelines') - expect(page).to have_content 'Last edit' - end + flag_values = [true, false] + flag_values.each do |val| + before do + stub_feature_flags(restyle_login_page: val) + end - it 'preview sign-in page appearance' do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + it 'create new appearance' do + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + visit admin_application_settings_appearances_path - visit admin_application_settings_appearances_path - click_link "Sign-in page" + fill_in 'appearance_title', with: 'MyCompany' + fill_in 'appearance_description', with: 'dev server' + fill_in 'appearance_new_project_guidelines', with: 'Custom project guidelines' + fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines' + click_button 'Update appearance settings' - expect(find('#login')).to be_disabled - expect(find('#password')).to be_disabled - expect(find('button')).to be_disabled + expect(page).to have_current_path admin_application_settings_appearances_path, ignore_query: true + expect(page).to have_content 'Appearance' - expect_custom_sign_in_appearance(appearance) - end + expect(page).to have_field('appearance_title', with: 'MyCompany') + expect(page).to have_field('appearance_description', with: 'dev server') + expect(page).to have_field('appearance_new_project_guidelines', with: 'Custom project guidelines') + expect(page).to have_field('appearance_profile_image_guidelines', with: 'Custom profile image guidelines') + expect(page).to have_content 'Last edit' + end - it 'preview new project page appearance', :js do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + it 'preview sign-in page appearance' do + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) - visit admin_application_settings_appearances_path - click_link "New project page" + visit admin_application_settings_appearances_path + click_link "Sign-in page" - expect_custom_new_project_appearance(appearance) - end + expect(find('#login')).to be_disabled + expect(find('#password')).to be_disabled + expect(find('button')).to be_disabled - context 'Custom system header and footer' do - before do + expect_custom_sign_in_appearance(appearance) + end + + it 'preview new project page appearance', :js do sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) - end - context 'when system header and footer messages are empty' do - it 'shows custom system header and footer fields' do - visit admin_application_settings_appearances_path + visit admin_application_settings_appearances_path + click_link "New project page" - expect(page).to have_field('appearance_header_message', with: '') - expect(page).to have_field('appearance_footer_message', with: '') - expect(page).to have_field('appearance_message_background_color') - expect(page).to have_field('appearance_message_font_color') - end + expect_custom_new_project_appearance(appearance) end - context 'when system header and footer messages are not empty' do + context 'Custom system header and footer' do before do - appearance.update!(header_message: 'Foo', footer_message: 'Bar') + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) end - it 'shows custom system header and footer fields' do - visit admin_application_settings_appearances_path + context 'when system header and footer messages are empty' do + it 'shows custom system header and footer fields' do + visit admin_application_settings_appearances_path - expect(page).to have_field('appearance_header_message', with: appearance.header_message) - expect(page).to have_field('appearance_footer_message', with: appearance.footer_message) - expect(page).to have_field('appearance_message_background_color') - expect(page).to have_field('appearance_message_font_color') + expect(page).to have_field('appearance_header_message', with: '') + expect(page).to have_field('appearance_footer_message', with: '') + expect(page).to have_field('appearance_message_background_color') + expect(page).to have_field('appearance_message_font_color') + end end - end - end - it 'custom sign-in page' do - visit new_user_session_path + context 'when system header and footer messages are not empty' do + before do + appearance.update!(header_message: 'Foo', footer_message: 'Bar') + end - expect_custom_sign_in_appearance(appearance) - end + it 'shows custom system header and footer fields' do + visit admin_application_settings_appearances_path - it 'custom new project page', :js do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - visit new_project_path - click_link 'Create blank project' + expect(page).to have_field('appearance_header_message', with: appearance.header_message) + expect(page).to have_field('appearance_footer_message', with: appearance.footer_message) + expect(page).to have_field('appearance_message_background_color') + expect(page).to have_field('appearance_message_font_color') + end + end + end - expect_custom_new_project_appearance(appearance) - end + it 'custom sign-in page' do + visit new_user_session_path - context 'Profile page with custom profile image guidelines' do - before do - sign_in(create(:admin)) + expect_custom_sign_in_appearance(appearance) + end + + it 'custom new project page', :js do + sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) - visit admin_application_settings_appearances_path - fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines, please :smile:!' - click_button 'Update appearance settings' + visit new_project_path + click_link 'Create blank project' + + expect_custom_new_project_appearance(appearance) end - it 'renders guidelines when set' do - sign_in create(:user) - visit profile_path + context 'Profile page with custom profile image guidelines' do + before do + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + visit admin_application_settings_appearances_path + fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines, please :smile:!' + click_button 'Update appearance settings' + end - expect(page).to have_content 'Custom profile image guidelines, please 😄!' + it 'renders guidelines when set' do + sign_in create(:user) + visit profile_path + + expect(page).to have_content 'Custom profile image guidelines, please 😄!' + end end - end - it 'appearance logo' do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - visit admin_application_settings_appearances_path + it 'appearance logo' do + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + visit admin_application_settings_appearances_path - attach_file(:appearance_logo, logo_fixture) - click_button 'Update appearance settings' - expect(page).to have_css(logo_selector) + attach_file(:appearance_logo, logo_fixture) + click_button 'Update appearance settings' + expect(page).to have_css(logo_selector) - click_link 'Remove logo' - expect(page).not_to have_css(logo_selector) - end + click_link 'Remove logo' + expect(page).not_to have_css(logo_selector) + end - it 'header logos' do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - visit admin_application_settings_appearances_path + it 'header logos' do + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + visit admin_application_settings_appearances_path - attach_file(:appearance_header_logo, logo_fixture) - click_button 'Update appearance settings' - expect(page).to have_css(header_logo_selector) + attach_file(:appearance_header_logo, logo_fixture) + click_button 'Update appearance settings' + expect(page).to have_css(header_logo_selector) - click_link 'Remove header logo' - expect(page).not_to have_css(header_logo_selector) - end + click_link 'Remove header logo' + expect(page).not_to have_css(header_logo_selector) + end - it 'Favicon' do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - visit admin_application_settings_appearances_path + it 'Favicon' do + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + visit admin_application_settings_appearances_path - attach_file(:appearance_favicon, logo_fixture) - click_button 'Update appearance settings' + attach_file(:appearance_favicon, logo_fixture) + click_button 'Update appearance settings' - expect(page).to have_css('.appearance-light-logo-preview') + expect(page).to have_css('.appearance-light-logo-preview') - click_link 'Remove favicon' + click_link 'Remove favicon' - expect(page).not_to have_css('.appearance-light-logo-preview') + expect(page).not_to have_css('.appearance-light-logo-preview') - # allowed file types - attach_file(:appearance_favicon, Rails.root.join('spec', 'fixtures', 'sanitized.svg')) - click_button 'Update appearance settings' + # allowed file types + attach_file(:appearance_favicon, Rails.root.join('spec', 'fixtures', 'sanitized.svg')) + click_button 'Update appearance settings' - expect(page).to have_content 'Favicon You are not allowed to upload "svg" files, allowed types: png, ico' + expect(page).to have_content 'Favicon You are not allowed to upload "svg" files, allowed types: png, ico' + end end def expect_custom_sign_in_appearance(appearance) diff --git a/spec/features/admin/admin_mode/login_spec.rb b/spec/features/admin/admin_mode/login_spec.rb index 659f66a67d2..6b4c9adb096 100644 --- a/spec/features/admin/admin_mode/login_spec.rb +++ b/spec/features/admin/admin_mode/login_spec.rb @@ -13,248 +13,252 @@ RSpec.describe 'Admin Mode Login' do click_button 'Verify code' end - context 'with valid username/password' do - let(:user) { create(:admin, :two_factor) } - - context 'using one-time code' do - it 'blocks login if we reuse the same code immediately' do - gitlab_sign_in(user, remember: true) - - expect(page).to have_content('Two-Factor Authentication') - - repeated_otp = user.current_otp - enter_code(repeated_otp) - gitlab_enable_admin_mode_sign_in(user) - - expect(page).to have_content('Two-Factor Authentication') - - enter_code(repeated_otp) - - expect(page).to have_current_path admin_session_path, ignore_query: true - expect(page).to have_content('Invalid two-factor code') - end + flag_values = [true, false] + flag_values.each do |val| + before do + stub_feature_flags(restyle_login_page: val) + end + context 'with valid username/password' do + let(:user) { create(:admin, :two_factor) } - context 'not re-using codes' do - before do + context 'using one-time code' do + it 'blocks login if we reuse the same code immediately' do gitlab_sign_in(user, remember: true) expect(page).to have_content('Two-Factor Authentication') - enter_code(user.current_otp) + repeated_otp = user.current_otp + enter_code(repeated_otp) gitlab_enable_admin_mode_sign_in(user) expect(page).to have_content('Two-Factor Authentication') - end - it 'allows login with valid code' do - # Cannot reuse the TOTP - travel_to(30.seconds.from_now) do - enter_code(user.current_otp) + enter_code(repeated_otp) - expect(page).to have_current_path admin_root_path, ignore_query: true - expect(page).to have_content('Admin mode enabled') - end - end - - it 'blocks login with invalid code' do - # Cannot reuse the TOTP - travel_to(30.seconds.from_now) do - enter_code('foo') - - expect(page).to have_content('Invalid two-factor code') - end + expect(page).to have_current_path admin_session_path, ignore_query: true + expect(page).to have_content('Invalid two-factor code') end - it 'allows login with invalid code, then valid code' do - # Cannot reuse the TOTP - travel_to(30.seconds.from_now) do - enter_code('foo') + context 'not re-using codes' do + before do + gitlab_sign_in(user, remember: true) - expect(page).to have_content('Invalid two-factor code') + expect(page).to have_content('Two-factor authentication code') enter_code(user.current_otp) + gitlab_enable_admin_mode_sign_in(user) - expect(page).to have_current_path admin_root_path, ignore_query: true - expect(page).to have_content('Admin mode enabled') + expect(page).to have_content('Two-Factor Authentication') end - end - context 'using backup code' do - let(:codes) { user.generate_otp_backup_codes! } + it 'allows login with valid code' do + # Cannot reuse the TOTP + travel_to(30.seconds.from_now) do + enter_code(user.current_otp) - before do - expect(codes.size).to eq 10 + expect(page).to have_current_path admin_root_path, ignore_query: true + expect(page).to have_content('Admin mode enabled') + end + end + + it 'blocks login with invalid code' do + # Cannot reuse the TOTP + travel_to(30.seconds.from_now) do + enter_code('foo') - # Ensure the generated codes get saved - user.save! + expect(page).to have_content('Invalid two-factor code') + end end - context 'with valid code' do - it 'allows login' do - enter_code(codes.sample) + it 'allows login with invalid code, then valid code' do + # Cannot reuse the TOTP + travel_to(30.seconds.from_now) do + enter_code('foo') + + expect(page).to have_content('Invalid two-factor code') + + enter_code(user.current_otp) expect(page).to have_current_path admin_root_path, ignore_query: true expect(page).to have_content('Admin mode enabled') end - - it 'invalidates the used code' do - expect { enter_code(codes.sample) } - .to change { user.reload.otp_backup_codes.size }.by(-1) - end end - context 'with invalid code' do - it 'blocks login' do - code = codes.sample - expect(user.invalidate_otp_backup_code!(code)).to eq true + context 'using backup code' do + let(:codes) { user.generate_otp_backup_codes! } + + before do + expect(codes.size).to eq 10 + # Ensure the generated codes get saved user.save! - expect(user.reload.otp_backup_codes.size).to eq 9 + end + + context 'with valid code' do + it 'allows login' do + enter_code(codes.sample) - enter_code(code) + expect(page).to have_current_path admin_root_path, ignore_query: true + expect(page).to have_content('Admin mode enabled') + end - expect(page).to have_content('Invalid two-factor code.') + it 'invalidates the used code' do + expect { enter_code(codes.sample) } + .to change { user.reload.otp_backup_codes.size }.by(-1) + end end - end - end - end - end - context 'when logging in via omniauth' do - let(:user) { create(:omniauth_user, :admin, :two_factor, extern_uid: 'my-uid', provider: 'saml', password_automatically_set: false)} - let(:mock_saml_response) do - File.read('spec/fixtures/authentication/saml_response.xml') - end + context 'with invalid code' do + it 'blocks login' do + code = codes.sample + expect(user.invalidate_otp_backup_code!(code)).to eq true + + user.save! + expect(user.reload.otp_backup_codes.size).to eq 9 + + enter_code(code) - before do - stub_omniauth_saml_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'], - providers: [mock_saml_config_with_upstream_two_factor_authn_contexts]) + expect(page).to have_content('Invalid two-factor code.') + end + end + end + end end - context 'when authn_context is worth two factors' do + context 'when logging in via omniauth' do + let(:user) { create(:omniauth_user, :admin, :two_factor, extern_uid: 'my-uid', provider: 'saml', password_automatically_set: false) } let(:mock_saml_response) do File.read('spec/fixtures/authentication/saml_response.xml') - .gsub('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', - 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS') end - it 'signs user in without prompting for second factor' do - sign_in_using_saml! - - expect(page).not_to have_content('Two-Factor Authentication') - - enable_admin_mode_using_saml! - - expect(page).not_to have_content('Two-Factor Authentication') - expect(page).to have_current_path admin_root_path, ignore_query: true - expect(page).to have_content('Admin mode enabled') + before do + stub_omniauth_saml_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'], providers: [mock_saml_config_with_upstream_two_factor_authn_contexts]) end - end - context 'when two factor authentication is required' do - it 'shows 2FA prompt after omniauth login' do - sign_in_using_saml! - - expect(page).to have_content('Two-Factor Authentication') - enter_code(user.current_otp) + context 'when authn_context is worth two factors' do + let(:mock_saml_response) do + File.read('spec/fixtures/authentication/saml_response.xml') + .gsub('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', + 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS') + end - enable_admin_mode_using_saml! + it 'signs user in without prompting for second factor' do + sign_in_using_saml! - expect(page).to have_content('Two-Factor Authentication') + expect(page).not_to have_content('Two-Factor Authentication') - # Cannot reuse the TOTP - travel_to(30.seconds.from_now) do - enter_code(user.current_otp) + enable_admin_mode_using_saml! + expect(page).not_to have_content('Two-Factor Authentication') expect(page).to have_current_path admin_root_path, ignore_query: true expect(page).to have_content('Admin mode enabled') end end - end - def sign_in_using_saml! - gitlab_sign_in_via('saml', user, 'my-uid', mock_saml_response) - end + context 'when two factor authentication is required' do + it 'shows 2FA prompt after omniauth login' do + sign_in_using_saml! - def enable_admin_mode_using_saml! - gitlab_enable_admin_mode_sign_in_via('saml', user, 'my-uid', mock_saml_response) - end - end + expect(page).to have_content('Two-Factor Authentication') + enter_code(user.current_otp) - context 'when logging in via ldap' do - let(:uid) { 'my-uid' } - let(:provider_label) { 'Main LDAP' } - let(:provider_name) { 'main' } - let(:provider) { "ldap#{provider_name}" } - let(:ldap_server_config) do - { - 'label' => provider_label, - 'provider_name' => provider, - 'attributes' => {}, - 'encryption' => 'plain', - 'uid' => 'uid', - 'base' => 'dc=example,dc=com' - } - end + enable_admin_mode_using_saml! + + expect(page).to have_content('Two-Factor Authentication') - let(:user) { create(:omniauth_user, :admin, :two_factor, extern_uid: uid, provider: provider) } + # Cannot reuse the TOTP + travel_to(30.seconds.from_now) do + enter_code(user.current_otp) - before do - setup_ldap(provider, user, uid, ldap_server_config) + expect(page).to have_current_path admin_root_path, ignore_query: true + expect(page).to have_content('Admin mode enabled') + end + end + end + + def sign_in_using_saml! + gitlab_sign_in_via('saml', user, 'my-uid', mock_saml_response) + end + + def enable_admin_mode_using_saml! + gitlab_enable_admin_mode_sign_in_via('saml', user, 'my-uid', mock_saml_response) + end end - context 'when two factor authentication is required' do - it 'shows 2FA prompt after ldap login' do - sign_in_using_ldap!(user, provider_label) + context 'when logging in via ldap' do + let(:uid) { 'my-uid' } + let(:provider_label) { 'Main LDAP' } + let(:provider_name) { 'main' } + let(:provider) { "ldap#{provider_name}" } + let(:ldap_server_config) do + { + 'label' => provider_label, + 'provider_name' => provider, + 'attributes' => {}, + 'encryption' => 'plain', + 'uid' => 'uid', + 'base' => 'dc=example,dc=com' + } + end - expect(page).to have_content('Two-Factor Authentication') + let(:user) { create(:omniauth_user, :admin, :two_factor, extern_uid: uid, provider: provider) } - enter_code(user.current_otp) - enable_admin_mode_using_ldap!(user) + before do + setup_ldap(provider, user, uid, ldap_server_config) + end - expect(page).to have_content('Two-Factor Authentication') + context 'when two factor authentication is required' do + it 'shows 2FA prompt after ldap login' do + sign_in_using_ldap!(user, provider_label) + expect(page).to have_content('Two-Factor Authentication') - # Cannot reuse the TOTP - travel_to(30.seconds.from_now) do enter_code(user.current_otp) + enable_admin_mode_using_ldap!(user) - expect(page).to have_current_path admin_root_path, ignore_query: true - expect(page).to have_content('Admin mode enabled') + expect(page).to have_content('Two-Factor Authentication') + + # Cannot reuse the TOTP + travel_to(30.seconds.from_now) do + enter_code(user.current_otp) + + expect(page).to have_current_path admin_root_path, ignore_query: true + expect(page).to have_content('Admin mode enabled') + end end end - end - def setup_ldap(provider, user, uid, ldap_server_config) - stub_ldap_setting(enabled: true) + def setup_ldap(provider, user, uid, ldap_server_config) + stub_ldap_setting(enabled: true) - allow(::Gitlab::Auth::Ldap::Config).to receive_messages(enabled: true, servers: [ldap_server_config]) - allow(Gitlab::Auth::OAuth::Provider).to receive_messages(providers: [provider.to_sym]) + allow(::Gitlab::Auth::Ldap::Config).to receive_messages(enabled: true, servers: [ldap_server_config]) + allow(Gitlab::Auth::OAuth::Provider).to receive_messages(providers: [provider.to_sym]) - Ldap::OmniauthCallbacksController.define_providers! - Rails.application.reload_routes! + Ldap::OmniauthCallbacksController.define_providers! + Rails.application.reload_routes! - mock_auth_hash(provider, uid, user.email) - allow(Gitlab::Auth::Ldap::Access).to receive(:allowed?).with(user).and_return(true) + mock_auth_hash(provider, uid, user.email) + allow(Gitlab::Auth::Ldap::Access).to receive(:allowed?).with(user).and_return(true) - allow_any_instance_of(ActionDispatch::Routing::RoutesProxy) - .to receive(:"user_#{provider}_omniauth_callback_path") - .and_return("/users/auth/#{provider}/callback") - end + allow_any_instance_of(ActionDispatch::Routing::RoutesProxy) + .to receive(:"user_#{provider}_omniauth_callback_path") + .and_return("/users/auth/#{provider}/callback") + end - def sign_in_using_ldap!(user, provider_label) - visit new_user_session_path - click_link provider_label - fill_in 'username', with: user.username - fill_in 'password', with: user.password - click_button 'Sign in' - end + def sign_in_using_ldap!(user, provider_label) + visit new_user_session_path + click_link provider_label + fill_in 'username', with: user.username + fill_in 'password', with: user.password + click_button 'Sign in' + end - def enable_admin_mode_using_ldap!(user) - visit new_admin_session_path - click_link provider_label - fill_in 'username', with: user.username - fill_in 'password', with: user.password - click_button 'Enter Admin Mode' + def enable_admin_mode_using_ldap!(user) + visit new_admin_session_path + click_link provider_label + fill_in 'username', with: user.username + fill_in 'password', with: user.password + click_button 'Enter Admin Mode' + end end end end diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb index e5df6cc0fd3..236327ea687 100644 --- a/spec/features/admin/users/users_spec.rb +++ b/spec/features/admin/users/users_spec.rb @@ -357,7 +357,7 @@ RSpec.describe 'Admin::Users' do end it 'creates new user' do - expect { click_button 'Create user' }.to change {User.count}.by(1) + expect { click_button 'Create user' }.to change { User.count }.by(1) end it 'applies defaults to user' do @@ -400,7 +400,7 @@ RSpec.describe 'Admin::Users' do let_it_be(:user_username) { 'Bing bang' } it "doesn't create the user and shows an error message" do - expect { click_button 'Create user' }.to change {User.count}.by(0) + expect { click_button 'Create user' }.to change { User.count }.by(0) expect(page).to have_content('The form contains the following error') expect(page).to have_content('Username can contain only letters, digits') diff --git a/spec/features/admin_variables_spec.rb b/spec/features/admin_variables_spec.rb new file mode 100644 index 00000000000..174d4567520 --- /dev/null +++ b/spec/features/admin_variables_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Instance variables', :js do + let(:admin) { create(:admin) } + let(:page_path) { ci_cd_admin_application_settings_path } + + let_it_be(:variable) { create(:ci_instance_variable, key: 'test_key', value: 'test_value', masked: true) } + + before do + stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + wait_for_requests + end + + context 'with disabled ff `ci_variable_settings_graphql' do + before do + stub_feature_flags(ci_variable_settings_graphql: false) + visit page_path + end + + it_behaves_like 'variable list', isAdmin: true + end + + context 'with enabled ff `ci_variable_settings_graphql' do + before do + visit page_path + end + + it_behaves_like 'variable list', isAdmin: true + end +end diff --git a/spec/features/boards/board_filters_spec.rb b/spec/features/boards/board_filters_spec.rb index 537b677cbd0..2e4dc4a29fc 100644 --- a/spec/features/boards/board_filters_spec.rb +++ b/spec/features/boards/board_filters_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'Issue board filters', :js do let_it_be(:award_emoji1) { create(:award_emoji, name: 'thumbsup', user: user, awardable: issue_1) } let(:filtered_search) { find('[data-testid="issue-board-filtered-search"]') } - let(:filter_input) { find('.gl-filtered-search-term-input')} + let(:filter_input) { find('.gl-filtered-search-term-input') } let(:filter_dropdown) { find('.gl-filtered-search-suggestion-list') } let(:filter_first_suggestion) { find('.gl-filtered-search-suggestion-list').first('.gl-filtered-search-suggestion') } let(:filter_submit) { find('.gl-search-box-by-click-search-button') } @@ -164,7 +164,7 @@ RSpec.describe 'Issue board filters', :js do end describe 'filters by type' do - let_it_be(:incident) { create(:incident, project: project)} + let_it_be(:incident) { create(:incident, project: project) } before do set_filter('type') diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index e8321adeb42..f279af90aa3 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -135,6 +135,7 @@ RSpec.describe 'Project issue boards', :js do 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("window.scrollTo(0, document.body.scrollHeight)") evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") end @@ -144,6 +145,7 @@ RSpec.describe 'Project issue boards', :js do 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("window.scrollTo(0, document.body.scrollHeight)") evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") end @@ -153,6 +155,7 @@ RSpec.describe 'Project issue boards', :js do 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("window.scrollTo(0, document.body.scrollHeight)") evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") end @@ -272,7 +275,7 @@ RSpec.describe 'Project issue boards', :js do context 'issue card' do it 'shows assignee' do page.within(find('.board:nth-child(2)')) do - expect(page).to have_selector('.avatar', count: 1) + expect(page).to have_selector('.gl-avatar', count: 1) end end @@ -400,6 +403,7 @@ RSpec.describe 'Project issue boards', :js do 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("window.scrollTo(0, document.body.scrollHeight)") evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") end @@ -409,6 +413,7 @@ RSpec.describe 'Project issue boards', :js do 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("window.scrollTo(0, document.body.scrollHeight)") evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") end @@ -417,6 +422,7 @@ RSpec.describe 'Project issue boards', :js do 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("window.scrollTo(0, document.body.scrollHeight)") evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") end diff --git a/spec/features/boards/reload_boards_on_browser_back_spec.rb b/spec/features/boards/reload_boards_on_browser_back_spec.rb index 6a09e3c9506..7fa440befc1 100644 --- a/spec/features/boards/reload_boards_on_browser_back_spec.rb +++ b/spec/features/boards/reload_boards_on_browser_back_spec.rb @@ -3,9 +3,9 @@ require 'spec_helper' RSpec.describe 'Ensure Boards do not show stale data on browser back', :js do - let(:project) {create(:project, :public)} - let(:board) {create(:board, project: project)} - let(:user) {create(:user)} + let(:project) { create(:project, :public) } + let(:board) { create(:board, project: project) } + let(:user) { create(:user) } context 'authorized user' do before do diff --git a/spec/features/clusters/create_agent_spec.rb b/spec/features/clusters/create_agent_spec.rb index b879ae645f7..c44741b756b 100644 --- a/spec/features/clusters/create_agent_spec.rb +++ b/spec/features/clusters/create_agent_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe 'Cluster agent registration', :js do let_it_be(:project) { create(:project, :custom_repo, files: { '.gitlab/agents/example-agent-1/config.yaml' => '' }) } let_it_be(:current_user) { create(:user, maintainer_projects: [project]) } + let_it_be(:token) { Devise.friendly_token } before do allow(Gitlab::Kas).to receive(:enabled?).and_return(true) @@ -18,7 +19,7 @@ RSpec.describe 'Cluster agent registration', :js do allow(client).to receive(:get_connected_agents).and_return([]) end - allow(Devise).to receive(:friendly_token).and_return('example-agent-token') + allow(Devise).to receive(:friendly_token).and_return(token) sign_in(current_user) visit project_clusters_path(project) @@ -33,7 +34,7 @@ RSpec.describe 'Cluster agent registration', :js do click_button('Register') expect(page).to have_content('You cannot see this token again after you close this window.') - expect(page).to have_content('example-agent-token') + expect(page).to have_content(token) expect(page).to have_content('helm upgrade --install') expect(page).to have_content('example-agent-2') diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb index 03d61020ff0..7714783172f 100644 --- a/spec/features/cycle_analytics_spec.rb +++ b/spec/features/cycle_analytics_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe 'Value Stream Analytics', :js do + include CycleAnalyticsHelpers + let_it_be(:user) { create(:user) } let_it_be(:guest) { create(:user) } let_it_be(:stage_table_selector) { '[data-testid="vsa-stage-table"]' } @@ -213,14 +215,20 @@ RSpec.describe 'Value Stream Analytics', :js do expect(page.find(metrics_selector)).not_to have_selector("#commits") end - it 'needs permissions to see restricted stages' do + it 'does not show restricted stages', :aggregate_failures do expect(find(stage_table_selector)).to have_content(issue.title) - click_stage('Code') - expect(find(stage_table_selector)).to have_content('You need permission.') + expect(page).to have_selector('.gl-path-nav-list-item', text: 'Issue') + + expect(page).to have_selector('.gl-path-nav-list-item', text: 'Plan') + + expect(page).to have_selector('.gl-path-nav-list-item', text: 'Test') + + expect(page).to have_selector('.gl-path-nav-list-item', text: 'Staging') + + expect(page).not_to have_selector('.gl-path-nav-list-item', text: 'Code') - click_stage('Review') - expect(find(stage_table_selector)).to have_content('You need permission.') + expect(page).not_to have_selector('.gl-path-nav-list-item', text: 'Review') end end diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb index 1b349fa2276..d157d44bab7 100644 --- a/spec/features/dashboard/archived_projects_spec.rb +++ b/spec/features/dashboard/archived_projects_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe 'Dashboard Archived Project' do let(:user) { create :user } - let(:project) { create :project} + let(:project) { create :project } let(:archived_project) { create(:project, :archived) } before do diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb index f8b68be7f93..91901414dde 100644 --- a/spec/features/dashboard/issuables_counter_spec.rb +++ b/spec/features/dashboard/issuables_counter_spec.rb @@ -8,73 +8,41 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d let(:issue) { create(:issue, project: project) } let(:merge_request) { create(:merge_request, source_project: project) } - describe 'feature flag mr_attention_requests is disabled' do - before do - stub_feature_flags(mr_attention_requests: false) - - issue.assignees = [user] - merge_request.update!(assignees: [user]) - sign_in(user) - end - - it 'reflects dashboard issues count' do - visit issues_path - - expect_counters('issues', '1', n_("%d assigned issue", "%d assigned issues", 1) % 1) - - issue.assignees = [] - - user.invalidate_cache_counts + before do + issue.assignees = [user] + merge_request.update!(assignees: [user]) + sign_in(user) + end - travel_to(3.minutes.from_now) do - visit issues_path + it 'reflects dashboard issues count' do + visit issues_path - expect_counters('issues', '0', n_("%d assigned issue", "%d assigned issues", 0) % 0) - end - end - - it 'reflects dashboard merge requests count', :js do - visit merge_requests_path + expect_counters('issues', '1', n_("%d assigned issue", "%d assigned issues", 1) % 1) - expect_counters('merge_requests', '1', n_("%d merge request", "%d merge requests", 1) % 1) + issue.assignees = [] - merge_request.update!(assignees: []) + user.invalidate_cache_counts - user.invalidate_cache_counts - - travel_to(3.minutes.from_now) do - visit merge_requests_path + travel_to(3.minutes.from_now) do + visit issues_path - expect_counters('merge_requests', '0', n_("%d merge request", "%d merge requests", 0) % 0) - end + expect_counters('issues', '0', n_("%d assigned issue", "%d assigned issues", 0) % 0) end end - describe 'feature flag mr_attention_requests is enabled' do - before do - merge_request.update!(assignees: [user]) - - merge_request.find_assignee(user).update!(state: :attention_requested) - - user.invalidate_attention_requested_count - - sign_in(user) - end - - it 'reflects dashboard merge requests count', :js do - visit merge_requests_attention_path + it 'reflects dashboard merge requests count', :js do + visit merge_requests_path - expect_counters('merge_requests', '1', n_("%d merge request", "%d merge requests", 1) % 1) + expect_counters('merge_requests', '1', n_("%d merge request", "%d merge requests", 1) % 1) - merge_request.find_assignee(user).update!(state: :reviewed) + merge_request.update!(assignees: []) - user.invalidate_attention_requested_count + user.invalidate_cache_counts - travel_to(3.minutes.from_now) do - visit merge_requests_attention_path + travel_to(3.minutes.from_now) do + visit merge_requests_path - expect_counters('merge_requests', '0', n_("%d merge request", "%d merge requests", 0) % 0) - end + expect_counters('merge_requests', '0', n_("%d merge request", "%d merge requests", 0) % 0) end end @@ -86,10 +54,6 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d merge_requests_dashboard_path(assignee_username: user.username) end - def merge_requests_attention_path - merge_requests_dashboard_path(attention: user.username) - end - def expect_counters(issuable_type, count, badge_label) dashboard_count = find('.gl-tabs-nav li a.active') diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb index fd580b679ad..70f614cdcef 100644 --- a/spec/features/dashboard/merge_requests_spec.rb +++ b/spec/features/dashboard/merge_requests_spec.rb @@ -112,8 +112,8 @@ RSpec.describe 'Dashboard Merge Requests' do end it 'includes assigned and reviewers in badge' do - within("span[aria-label='#{n_("%d merge request", "%d merge requests", 0) % 0}']") do - expect(page).to have_content('0') + within("span[aria-label='#{n_("%d merge request", "%d merge requests", 3) % 3}']") do + expect(page).to have_content('3') end find('.dashboard-shortcuts-merge_requests').click diff --git a/spec/features/error_tracking/user_filters_errors_by_status_spec.rb b/spec/features/error_tracking/user_filters_errors_by_status_spec.rb index d5dbe259159..2ac43f67f64 100644 --- a/spec/features/error_tracking/user_filters_errors_by_status_spec.rb +++ b/spec/features/error_tracking/user_filters_errors_by_status_spec.rb @@ -10,8 +10,8 @@ RSpec.describe 'When a user filters Sentry errors by status', :js, :use_clean_ra let(:issues_api_url) { "#{sentry_api_urls.issues_url}?limit=20&query=is:unresolved" } let(:issues_api_url_filter) { "#{sentry_api_urls.issues_url}?limit=20&query=is:ignored" } - let(:auth_token) {{ 'Authorization' => 'Bearer access_token_123' }} - let(:return_header) {{ 'Content-Type' => 'application/json' }} + let(:auth_token) { { 'Authorization' => 'Bearer access_token_123' } } + let(:return_header) { { 'Content-Type' => 'application/json' } } before do stub_request(:get, issues_api_url).with(headers: auth_token) diff --git a/spec/features/group_variables_spec.rb b/spec/features/group_variables_spec.rb index 9af9baeb5bb..ab24162ad5a 100644 --- a/spec/features/group_variables_spec.rb +++ b/spec/features/group_variables_spec.rb @@ -23,7 +23,11 @@ RSpec.describe 'Group variables', :js do it_behaves_like 'variable list' end - # TODO: Uncomment when the new graphQL app for variable settings - # is enabled. - # it_behaves_like 'variable list' + context 'with enabled ff `ci_variable_settings_graphql' do + before do + visit page_path + end + + it_behaves_like 'variable list' + end end diff --git a/spec/features/groups/crm/contacts/create_spec.rb b/spec/features/groups/crm/contacts/create_spec.rb index d6c6e3f1745..b10b2afe35c 100644 --- a/spec/features/groups/crm/contacts/create_spec.rb +++ b/spec/features/groups/crm/contacts/create_spec.rb @@ -22,7 +22,9 @@ RSpec.describe 'Create a CRM contact', :js do fill_in 'description', with: 'VIP' click_button 'Save changes' - expect(page).to have_content 'gitlab@example.com' + wait_for_requests + + expect(group.contacts.first.email).to eq('gitlab@example.com') expect(page).to have_current_path("#{group_crm_contacts_path(group)}/", ignore_query: true) end end diff --git a/spec/features/groups/group_runners_spec.rb b/spec/features/groups/group_runners_spec.rb index a129db6cb6f..b98c94b030d 100644 --- a/spec/features/groups/group_runners_spec.rb +++ b/spec/features/groups/group_runners_spec.rb @@ -149,77 +149,39 @@ RSpec.describe "Group Runners" do create(:ci_runner, :group, groups: [group], description: 'runner-foo', contacted_at: Time.zone.now) end - context 'when group_runner_view_ui is disabled' do - before do - stub_feature_flags(group_runner_view_ui: false) - end - - it 'user edits the runner to be protected' do - visit edit_group_runner_path(group, runner) + it 'user views runner details' do + visit group_runner_path(group, runner) - expect(page.find_field('runner[access_level]')).not_to be_checked - - check 'runner_access_level' - click_button 'Save changes' - - expect(page).to have_content 'Protected Yes' - end - - context 'when a runner has a tag' do - before do - runner.update!(tag_list: ['tag']) - end + expect(page).to have_content "#{s_('Runners|Description')} runner-foo" + end - it 'user edits runner not to run untagged jobs' do - visit edit_group_runner_path(group, runner) + it 'user edits the runner to be protected' do + visit edit_group_runner_path(group, runner) - expect(page.find_field('runner[run_untagged]')).to be_checked + expect(page.find_field('runner[access_level]')).not_to be_checked - uncheck 'runner_run_untagged' - click_button 'Save changes' + check 'runner_access_level' + click_button _('Save changes') - expect(page).to have_content 'Can run untagged jobs No' - end - end + expect(page).to have_content "#{s_('Runners|Configuration')} #{s_('Runners|Protected')}" end - context 'when group_runner_view_ui is enabled' do + context 'when a runner has a tag' do before do - stub_feature_flags(group_runner_view_ui: true) + runner.update!(tag_list: ['tag1']) end - it 'user views runner details' do - visit group_runner_path(group, runner) - - expect(page).to have_content "#{s_('Runners|Description')} runner-foo" - end - - it 'user edits the runner to be protected' do + it 'user edits runner not to run untagged jobs' do visit edit_group_runner_path(group, runner) - expect(page.find_field('runner[access_level]')).not_to be_checked + page.find_field('runner[tag_list]').set('tag1, tag2') - check 'runner_access_level' + uncheck 'runner_run_untagged' click_button _('Save changes') - expect(page).to have_content "#{s_('Runners|Configuration')} #{s_('Runners|Protected')}" - end - - context 'when a runner has a tag' do - before do - runner.update!(tag_list: ['tag']) - end - - it 'user edits runner not to run untagged jobs' do - visit edit_group_runner_path(group, runner) - - page.find_field('runner[tag_list]').set('tag, tag2') - - uncheck 'runner_run_untagged' - click_button _('Save changes') - - expect(page).to have_content "#{s_('Runners|Tags')} tag tag2" - end + # Tags can be in any order + expect(page).to have_content /#{s_('Runners|Tags')}.*tag1/ + expect(page).to have_content /#{s_('Runners|Tags')}.*tag2/ end end end diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb index c86705832b1..eec07c84cde 100644 --- a/spec/features/groups/issues_spec.rb +++ b/spec/features/groups/issues_spec.rb @@ -7,12 +7,12 @@ RSpec.describe 'Group issues page' do include DragTo let(:group) { create(:group) } - let(:project) { create(:project, :public, group: group)} + let(:project) { create(:project, :public, group: group) } let(:project_with_issues_disabled) { create(:project, :issues_disabled, group: group) } let(:path) { issues_group_path(group) } context 'with shared examples', :js do - let(:issuable) { create(:issue, project: project, title: "this is my created issuable")} + let(:issuable) { create(:issue, project: project, title: "this is my created issuable") } include_examples 'project features apply to issuables', Issue @@ -68,7 +68,7 @@ RSpec.describe 'Group issues page' do context 'issues list', :js do let(:subgroup) { create(:group, parent: group) } - let(:subgroup_project) { create(:project, :public, group: subgroup)} + let(:subgroup_project) { create(:project, :public, group: subgroup) } let(:user_in_group) { create(:group_member, :maintainer, user: create(:user), group: group ).user } let!(:issue) { create(:issue, project: project, title: 'root group issue') } let!(:subgroup_issue) { create(:issue, project: subgroup_project, title: 'subgroup issue') } diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb index 468001c3be6..5f28afc23f1 100644 --- a/spec/features/groups/members/manage_members_spec.rb +++ b/spec/features/groups/members/manage_members_spec.rb @@ -74,8 +74,8 @@ RSpec.describe 'Groups > Members > Manage members' do invite_member(user1.name, role: 'Reporter', refresh: false) - expect(page).to have_selector(invite_modal_selector) - expect(page).to have_content("not authorized to update member") + invite_modal = page.find(invite_modal_selector) + expect(invite_modal).to have_content("not authorized to update member") page.refresh diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 9a1e216c6d2..d814906a274 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -84,7 +84,7 @@ RSpec.describe 'Group show page' do it 'shows `Create new subgroup` link' do expect(page).to have_link( s_('GroupsEmptyState|Create new subgroup'), - href: new_group_path(parent_id: group.id) + href: new_group_path(parent_id: group.id, anchor: 'create-group-pane') ) end @@ -97,28 +97,43 @@ RSpec.describe 'Group show page' do end end - context 'when a public project is shared with a private group' do - let_it_be(:private_group) { create(:group, :private) } + context 'visibility warning popover' do let_it_be(:public_project) { create(:project, :public) } - let_it_be(:project_group_link) { create(:project_group_link, group: private_group, project: public_project) } - before do - private_group.add_owner(user) - sign_in(user) - end + shared_examples 'it shows warning popover' do + it 'shows warning popover', :js do + group_to_share_with.add_owner(user) + sign_in(user) + visit group_path(group_to_share_with) + + click_link _('Shared projects') + + wait_for_requests - it 'shows warning popover', :js do - visit group_path(private_group) + page.within("[data-testid=\"group-overview-item-#{public_project.id}\"]") do + click_button _('Less restrictive visibility') + end + + expect(page).to have_content _('Project visibility level is less restrictive than the group settings.') + end + end - click_link _('Shared projects') + context 'when a public project is shared with a private group' do + let_it_be(:group_to_share_with) { create(:group, :private) } + let_it_be(:project_group_link) do + create(:project_group_link, group: group_to_share_with, project: public_project) + end - wait_for_requests + include_examples 'it shows warning popover' + end - page.within("[data-testid=\"group-overview-item-#{public_project.id}\"]") do - click_button _('Less restrictive visibility') + context 'when a public project is shared with an internal group' do + let_it_be(:group_to_share_with) { create(:group, :internal) } + let_it_be(:project_group_link) do + create(:project_group_link, group: group_to_share_with, project: public_project) end - expect(page).to have_content _('Project visibility level is less restrictive than the group settings.') + include_examples 'it shows warning popover' end end diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index ece6167b193..c93ed01b873 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -221,14 +221,13 @@ RSpec.describe 'Group' do let(:user) { create(:admin) } before do - visit new_group_path(parent_id: group.id) + visit new_group_path(parent_id: group.id, anchor: 'create-group-pane') end context 'when admin mode is enabled', :enable_admin_mode do it 'creates a nested group' do - click_link 'Create group' - fill_in 'Group name', with: 'bar' - click_button 'Create group' + fill_in 'Subgroup name', with: 'bar' + click_button 'Create subgroup' expect(page).to have_current_path(group_path('foo/bar'), ignore_query: true) expect(page).to have_selector 'h1', text: 'bar' @@ -237,7 +236,7 @@ RSpec.describe 'Group' do context 'when admin mode is disabled' do it 'is not allowed' do - expect(page).not_to have_button('Create group') + expect(page).not_to have_button('Create subgroup') end end end @@ -250,11 +249,10 @@ RSpec.describe 'Group' do sign_out(:user) sign_in(user) - visit new_group_path(parent_id: group.id) - click_link 'Create group' + visit new_group_path(parent_id: group.id, anchor: 'create-group-pane') - fill_in 'Group name', with: 'bar' - click_button 'Create group' + fill_in 'Subgroup name', with: 'bar' + click_button 'Create subgroup' expect(page).to have_current_path(group_path('foo/bar'), ignore_query: true) expect(page).to have_selector 'h1', text: 'bar' @@ -268,7 +266,7 @@ RSpec.describe 'Group' do end context 'when creating subgroup' do - let(:path) { new_group_path(parent_id: group.id) } + let(:path) { new_group_path(parent_id: group.id, anchor: 'create-group-pane') } it 'does not render recaptcha' do visit path @@ -278,24 +276,50 @@ RSpec.describe 'Group' do end end + context 'when many parent groups are available' do + let_it_be(:group2) { create(:group, path: 'foo2') } + let_it_be(:group3) { create(:group, path: 'foo3') } + + before do + group.add_owner(user) + group2.add_maintainer(user) + group3.add_developer(user) + visit new_group_path(parent_id: group.id, anchor: 'create-group-pane') + end + + it 'creates private subgroup' do + fill_in 'Subgroup name', with: 'bar' + click_button 'foo' + + expect(page).to have_css('[data-testid="select_group_dropdown_item"]', text: 'foo2') + expect(page).not_to have_css('[data-testid="select_group_dropdown_item"]', text: 'foo3') + + click_button 'foo2' + click_button 'Create subgroup' + + expect(page).to have_current_path(group_path('foo2/bar'), ignore_query: true) + expect(page).to have_selector('h1', text: 'bar') + expect(page).to have_selector('.visibility-icon [data-testid="lock-icon"]') + end + end + describe 'real-time group url validation', :js do let_it_be(:subgroup) { create(:group, path: 'sub', parent: group) } before do group.add_owner(user) - visit new_group_path(parent_id: group.id) - click_link 'Create group' + visit new_group_path(parent_id: group.id, anchor: 'create-group-pane') end it 'shows a message if group url is available' do - fill_in 'Group URL', with: group.path + fill_in 'Subgroup slug', with: group.path wait_for_requests expect(page).to have_content('Group path is available') end it 'shows an error if group url is taken' do - fill_in 'Group URL', with: subgroup.path + fill_in 'Subgroup slug', with: subgroup.path wait_for_requests expect(page).to have_content("Group path is unavailable. Path has been replaced with a suggested available path.") @@ -308,7 +332,7 @@ RSpec.describe 'Group' do sign_out(:user) sign_in(create(:user)) - visit new_group_path(parent_id: group.id) + visit new_group_path(parent_id: group.id, anchor: 'create-group-pane') expect(page).to have_title('Not Found') expect(page).to have_content('Page Not Found') @@ -354,7 +378,7 @@ RSpec.describe 'Group' do end it 'removes group', :sidekiq_might_not_need_inline do - expect { remove_with_confirm('Remove group', group.path) }.to change {Group.count}.by(-1) + expect { remove_with_confirm('Remove group', group.path) }.to change { Group.count }.by(-1) expect(group.members.all.count).to be_zero expect(page).to have_content "scheduled for deletion" end @@ -507,8 +531,8 @@ RSpec.describe 'Group' do let_it_be(:storage_enforcement_date) { Date.today + 30 } before do - allow_next_found_instance_of(Group) do |grp| - allow(grp).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) + allow_next_found_instance_of(Group) do |group| + allow(group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) end end @@ -518,8 +542,8 @@ RSpec.describe 'Group' do end it 'does not display the banner in a paid group page' do - allow_next_found_instance_of(Group) do |grp| - allow(grp).to receive(:paid?).and_return(true) + allow_next_found_instance_of(Group) do |group| + allow(group).to receive(:paid?).and_return(true) end visit group_path(group) expect_page_not_to_have_storage_enforcement_banner @@ -534,8 +558,8 @@ RSpec.describe 'Group' do expect_page_not_to_have_storage_enforcement_banner storage_enforcement_date = Date.today + 13 - allow_next_found_instance_of(Group) do |grp| - allow(grp).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) + allow_next_found_instance_of(Group) do |group| + allow(group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) end page.refresh expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) @@ -543,8 +567,12 @@ RSpec.describe 'Group' do end context 'with storage_enforcement_date not set' do - # This test should break and be rewritten after the implementation of the storage_enforcement_date - # TBD: https://gitlab.com/gitlab-org/gitlab/-/issues/350632 + before do + allow_next_found_instance_of(Group) do |group| + allow(group).to receive(:storage_enforcement_date).and_return(nil) + end + end + it 'does not display the banner in the group page' do stub_feature_flags(namespace_storage_limit_bypass_date_check: false) visit group_path(group) @@ -554,10 +582,10 @@ RSpec.describe 'Group' do end def expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) - expect(page).to have_text "From #{storage_enforcement_date} storage limits will apply to this namespace" + expect(page).to have_text "Effective #{storage_enforcement_date}, namespace storage limits will apply" end def expect_page_not_to_have_storage_enforcement_banner - expect(page).not_to have_text "storage limits will apply to this namespace" + expect(page).not_to have_text "namespace storage limits will apply" end end diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb index fe804dc52d7..1baa97096d9 100644 --- a/spec/features/invites_spec.rb +++ b/spec/features/invites_spec.rb @@ -182,12 +182,14 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do context 'email confirmation disabled' do let(:send_email_confirmation) { false } - it 'signs up and redirects to the most recent membership activity page with all the projects/groups invitations automatically accepted' do - fill_in_sign_up_form(new_user) - fill_in_welcome_form + context 'the user signs up for an account with the invitation email address' do + it 'redirects to the most recent membership activity page with all the projects/groups invitations automatically accepted' do + fill_in_sign_up_form(new_user) + fill_in_welcome_form - expect(page).to have_current_path(activity_group_path(group), ignore_query: true) - expect(page).to have_content('You have been granted Owner access to group Owned.') + expect(page).to have_current_path(activity_group_path(group), ignore_query: true) + expect(page).to have_content('You have been granted Owner access to group Owned.') + end end context 'the user sign-up using a different email address' do @@ -227,11 +229,13 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do end end - it 'signs up and redirects to the group activity page with all the project/groups invitation automatically accepted' do - fill_in_sign_up_form(new_user) - fill_in_welcome_form + context 'the user signs up for an account with the invitation email address' do + it 'redirects to the most recent membership activity page with all the projects/groups invitations automatically accepted' do + fill_in_sign_up_form(new_user) + fill_in_welcome_form - expect(page).to have_current_path(activity_group_path(group), ignore_query: true) + expect(page).to have_current_path(activity_group_path(group), ignore_query: true) + end end context 'the user sign-up using a different email address' do diff --git a/spec/features/issuables/user_sees_sidebar_spec.rb b/spec/features/issuables/user_sees_sidebar_spec.rb index 04bf704b6a4..66ed6044de6 100644 --- a/spec/features/issuables/user_sees_sidebar_spec.rb +++ b/spec/features/issuables/user_sees_sidebar_spec.rb @@ -8,7 +8,7 @@ RSpec.describe 'Issue Sidebar on Mobile' do let(:project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } let(:issue) { create(:issue, project: project) } - let!(:user) { create(:user)} + let!(:user) { create(:user) } before do sign_in(user) diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index 0700423983f..e749c555dcf 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -319,6 +319,13 @@ RSpec.describe 'New/edit issue', :js do end end end + + describe 'when repository contains CONTRIBUTING.md' do + it 'has contribution guidelines prompt' do + text = _('Please review the %{linkStart}contribution guidelines%{linkEnd} for this project.') % { linkStart: nil, linkEnd: nil } + expect(find('#new_issue')).to have_text(text) + end + end end describe 'new issue with query parameters' do diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb index 8732e2ecff2..fa4ce6fe1c1 100644 --- a/spec/features/issues/gfm_autocomplete_spec.rb +++ b/spec/features/issues/gfm_autocomplete_spec.rb @@ -414,7 +414,7 @@ RSpec.describe 'GFM autocomplete', :js do it 'shows all contacts' do page.within(find_autocomplete_menu) do - expected_data = contacts.map { |c| "#{c.first_name} #{c.last_name} #{c.email}"} + expected_data = contacts.map { |c| "#{c.first_name} #{c.last_name} #{c.email}" } expect(page.all('li').map(&:text)).to match_array(expected_data) end diff --git a/spec/features/issues/incident_issue_spec.rb b/spec/features/issues/incident_issue_spec.rb index d6ec7f1c539..56be1493ed2 100644 --- a/spec/features/issues/incident_issue_spec.rb +++ b/spec/features/issues/incident_issue_spec.rb @@ -30,7 +30,7 @@ RSpec.describe 'Incident Detail', :js do project.add_developer(user) sign_in(user) - visit project_issue_path(project, incident) + visit project_issues_incident_path(project, incident) wait_for_requests end @@ -49,72 +49,32 @@ RSpec.describe 'Incident Detail', :js do expect(incident_tabs).to have_content('Original alert: #1') end - aggregate_failures 'shows the Alert details tab' do - click_link 'Alert details' - - expect(incident_tabs).to have_content('"title": "Alert title"') - expect(incident_tabs).to have_content('"yet.another": 73') - end - end - end - - context 'when on summary tab' do - before do - click_link 'Summary' - end - - it 'shows the summary tab with all components' do - page.within('.issuable-details') do + aggregate_failures 'when on summary tab (default tab)' do hidden_items = find_all('.js-issue-widgets') # Linked Issues/MRs and comment box expect(hidden_items.count).to eq(2) - expect(hidden_items).to all(be_visible) - end - end - - it 'shows the edit title and description button' do - edit_button = find_all('[aria-label="Edit title and description"]') - - expect(edit_button).to all(be_visible) - end - end - - context 'when on alert details tab' do - before do - click_link 'Alert details' - end - - it 'does not show the linked issues and notes/comment components' do - page.within('.issuable-details') do - hidden_items = find_all('.js-issue-widgets') - # Linked Issues/MRs and comment box are hidden on page - expect(hidden_items.count).to eq(0) + edit_button = find_all('[aria-label="Edit title and description"]') + expect(edit_button).to all(be_visible) end - end - it 'does not show the edit title and description button' do - edit_button = find_all('[aria-label="Edit title and description"]') - - expect(edit_button.count).to eq(0) - end - end + aggregate_failures 'shows the Alert details tab' do + click_link 'Alert details' - context 'when on timeline events tab from incident route' do - before do - visit project_issues_incident_path(project, incident) - wait_for_requests - click_link 'Timeline' - end + expect(incident_tabs).to have_content('"title": "Alert title"') + expect(incident_tabs).to have_content('"yet.another": 73') - it 'does not show the linked issues and notes/comment components' do - page.within('.issuable-details') do + # does not show the linked issues and notes/comment components' do hidden_items = find_all('.js-issue-widgets') # Linked Issues/MRs and comment box are hidden on page expect(hidden_items.count).to eq(0) + + # does not show the edit title and description button + edit_button = find_all('[aria-label="Edit title and description"]') + expect(edit_button.count).to eq(0) end end end @@ -126,7 +86,7 @@ RSpec.describe 'Incident Detail', :js do click_link 'Timeline' end - it 'does not show the linked issues and notes/comment commponents' do + it 'does not show the linked issues and notes/comment components' do page.within('.issuable-details') do hidden_items = find_all('.js-issue-widgets') @@ -140,7 +100,7 @@ RSpec.describe 'Incident Detail', :js do before do stub_feature_flags(incident_timeline: false) - visit project_issue_path(project, incident) + visit project_issues_incident_path(project, incident) wait_for_requests end diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb index aaa478378a9..8819f085a5f 100644 --- a/spec/features/issues/issue_sidebar_spec.rb +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -84,8 +84,10 @@ RSpec.describe 'Issue Sidebar' do click_link user2.name end - find('.js-right-sidebar').click - find('.block.assignee .edit-link').click + within '.js-right-sidebar' do + find('.block.assignee').click(x: 0, y: 0) + find('.block.assignee .edit-link').click + end expect(page.all('.dropdown-menu-user li').length).to eq(1) expect(find('.dropdown-input-field').value).to eq(user2.name) @@ -182,7 +184,7 @@ RSpec.describe 'Issue Sidebar' do page.within '.dropdown-menu-user' do expect(page).not_to have_content 'Unassigned' - click_link user2.name + click_button user2.name end find('.participants').click diff --git a/spec/features/issues/related_issues_spec.rb b/spec/features/issues/related_issues_spec.rb index a95229d4f1b..818e99f2ec9 100644 --- a/spec/features/issues/related_issues_spec.rb +++ b/spec/features/issues/related_issues_spec.rb @@ -232,7 +232,9 @@ RSpec.describe 'Related issues', :js do it 'add related issue' do click_button 'Add a related issue' fill_in 'Paste issue link', with: "#{issue_b.to_reference(project)} " - click_button 'Add' + page.within('.linked-issues-card-body') do + click_button 'Add' + end wait_for_requests @@ -249,7 +251,9 @@ RSpec.describe 'Related issues', :js do it 'add cross-project related issue' do click_button 'Add a related issue' fill_in 'Paste issue link', with: "#{issue_project_b_a.to_reference(project)} " - click_button 'Add' + page.within('.linked-issues-card-body') do + click_button 'Add' + end wait_for_requests @@ -359,7 +363,9 @@ RSpec.describe 'Related issues', :js do it 'add related issue' do click_button 'Add a related issue' fill_in 'Paste issue link', with: "##{issue_d.iid} " - click_button 'Add' + page.within('.linked-issues-card-body') do + click_button 'Add' + end wait_for_requests @@ -375,7 +381,9 @@ RSpec.describe 'Related issues', :js do it 'add invalid related issue' do click_button 'Add a related issue' fill_in 'Paste issue link', with: '#9999999 ' - click_button 'Add' + page.within('.linked-issues-card-body') do + click_button 'Add' + end wait_for_requests @@ -390,7 +398,9 @@ RSpec.describe 'Related issues', :js do it 'add unauthorized related issue' do click_button 'Add a related issue' fill_in 'Paste issue link', with: "#{issue_project_unauthorized_a.to_reference(project)} " - click_button 'Add' + page.within('.linked-issues-card-body') do + click_button 'Add' + end wait_for_requests diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb index d63d21353e5..6a53c12eda3 100644 --- a/spec/features/issues/todo_spec.rb +++ b/spec/features/issues/todo_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'Manually create a todo item from issue', :js do let!(:project) { create(:project) } let!(:issue) { create(:issue, project: project) } - let!(:user) { create(:user)} + let!(:user) { create(:user) } before do project.add_maintainer(user) diff --git a/spec/features/issues/user_bulk_edits_issues_spec.rb b/spec/features/issues/user_bulk_edits_issues_spec.rb index 0533f1688e2..1ef2918adec 100644 --- a/spec/features/issues/user_bulk_edits_issues_spec.rb +++ b/spec/features/issues/user_bulk_edits_issues_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'Multiple issue updating from issues#index', :js do let!(:project) { create(:project) } let!(:issue) { create(:issue, project: project) } - let!(:user) { create(:user)} + let!(:user) { create(:user) } before do project.add_maintainer(user) diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb index 151d3c60fa2..e29911e3263 100644 --- a/spec/features/issues/user_creates_issue_spec.rb +++ b/spec/features/issues/user_creates_issue_spec.rb @@ -151,7 +151,7 @@ RSpec.describe "User creates issue" do click_button 'Cancel' end - expect(page).to have_button('Attach a file') + expect(page).to have_selector('[data-testid="button-attach-file"]') expect(page).not_to have_button('Cancel') expect(page).not_to have_selector('.uploading-progress-container', visible: true) end @@ -188,7 +188,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') # rubocop:disable QA/SelectorUsage + expect(page).to have_selector('[data-testid="issuable-milestone-dropdown"]') end end @@ -204,7 +204,7 @@ RSpec.describe "User creates issue" do end it 'shows the milestone select' do - expect(page).to have_selector('.qa-issuable-milestone-dropdown') # rubocop:disable QA/SelectorUsage + expect(page).to have_selector('[data-testid="issuable-milestone-dropdown"]') end it 'hides the incident help text' do @@ -265,7 +265,7 @@ RSpec.describe "User creates issue" do end it 'shows the milestone select' do - expect(page).to have_selector('.qa-issuable-milestone-dropdown') # rubocop:disable QA/SelectorUsage + expect(page).to have_selector('[data-testid="issuable-milestone-dropdown"]') end it 'hides the weight input' do diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb index 3b440002cb5..4eecb63c47e 100644 --- a/spec/features/issues/user_edits_issue_spec.rb +++ b/spec/features/issues/user_edits_issue_spec.rb @@ -155,7 +155,7 @@ RSpec.describe "Issues > User edits issue", :js do page.within '.block.labels' do # Remove `verisimilitude` label - within '.gl-label' do + within '.gl-label', text: 'verisimilitude' do click_button 'Remove label' end @@ -285,7 +285,7 @@ RSpec.describe "Issues > User edits issue", :js do end page.within '.dropdown-menu-user' do - click_link user.name + click_button user.name end page.within('.assignee') do @@ -306,7 +306,7 @@ RSpec.describe "Issues > User edits issue", :js do click_button('Edit') wait_for_requests - click_link user.name + click_button user.name find('[data-testid="title"]').click wait_for_requests diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb index 892b57bac5c..c86a2c32e2d 100644 --- a/spec/features/issues/user_interacts_with_awards_spec.rb +++ b/spec/features/issues/user_interacts_with_awards_spec.rb @@ -6,7 +6,7 @@ RSpec.describe 'User interacts with awards' do let(:user) { create(:user) } describe 'User interacts with awards in an issue', :js do - let(:issue) { create(:issue, project: project)} + let(:issue) { create(:issue, project: project) } let(:project) { create(:project) } before do diff --git a/spec/features/issues/user_uses_quick_actions_spec.rb b/spec/features/issues/user_uses_quick_actions_spec.rb index c6d743ed38f..d458c991668 100644 --- a/spec/features/issues/user_uses_quick_actions_spec.rb +++ b/spec/features/issues/user_uses_quick_actions_spec.rb @@ -18,7 +18,7 @@ RSpec.describe 'Issues > User uses quick actions', :js do let!(:label_feature) { create(:label, project: project, title: 'feature') } let!(:milestone) { create(:milestone, project: project, title: 'ASAP') } let(:issuable) { create(:issue, project: project) } - let(:source_issuable) { create(:issue, project: project, milestone: milestone, labels: [label_bug, label_feature])} + let(:source_issuable) { create(:issue, project: project, milestone: milestone, labels: [label_bug, label_feature]) } it_behaves_like 'close quick action', :issue it_behaves_like 'issuable time tracker', :issue diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb index d472134a2c7..b5bf9279371 100644 --- a/spec/features/markdown/copy_as_gfm_spec.rb +++ b/spec/features/markdown/copy_as_gfm_spec.rb @@ -109,10 +109,24 @@ RSpec.describe 'Copy as GFM', :js do <<~GFM, * [ ] Unchecked task * [x] Checked task + * [~] Inapplicable task + * [~] Inapplicable task with ~~del~~ and <s>strike</s> embedded GFM - <<~GFM + <<~GFM, 1. [ ] Unchecked ordered task 1. [x] Checked ordered task + 1. [~] Inapplicable ordered task + 1. [~] Inapplicable ordered task with ~~del~~ and <s>strike</s> embedded + GFM + <<~GFM + * [ ] Unchecked loose list task + * [x] Checked loose list task + * [~] Inapplicable loose list task + + With a paragraph + * [~] Inapplicable loose list task with ~~del~~ and <s>strike</s> embedded + + With a paragraph GFM ) @@ -605,7 +619,8 @@ RSpec.describe 'Copy as GFM', :js do '###### Heading', '**Bold**', '*Italics*', - '~~Strikethrough~~', + '~~Strikethrough (del)~~', + '<s>Strikethrough</s>', '---', # table <<~GFM, diff --git a/spec/features/markdown/gitlab_flavored_markdown_spec.rb b/spec/features/markdown/gitlab_flavored_markdown_spec.rb index da4208318eb..e831d1be608 100644 --- a/spec/features/markdown/gitlab_flavored_markdown_spec.rb +++ b/spec/features/markdown/gitlab_flavored_markdown_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe "GitLab Flavored Markdown" do + include CycleAnalyticsHelpers + let(:user) { create(:user) } let(:project) { create(:project) } let(:issue) { create(:issue, project: project) } diff --git a/spec/features/markdown/json_table_spec.rb b/spec/features/markdown/json_table_spec.rb new file mode 100644 index 00000000000..6b74dbac255 --- /dev/null +++ b/spec/features/markdown/json_table_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Rendering json:table code block in markdown', :js do + let_it_be(:project) { create(:project, :public) } + + it 'creates table correctly' do + description = <<~JSONTABLE + Hello world! + + ```json:table + { + "fields" : [ + {"key": "a", "label": "AA"}, + {"key": "b", "label": "BB"} + ], + "items" : [ + {"a": "11", "b": "22"}, + {"a": "211", "b": "222"} + ] + } + ``` + JSONTABLE + + issue = create(:issue, project: project, description: description) + + visit project_issue_path(project, issue) + + wait_for_requests + + within ".js-json-table table" do + headers = all("thead th").collect { |column| column.text.strip } + data = all("tbody td").collect { |column| column.text.strip } + + expect(headers).to eql(%w[AA BB]) + expect(data).to eql(%w[11 22 211 222]) + end + end +end diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb index fafaea8ac68..f892b01e624 100644 --- a/spec/features/merge_request/batch_comments_spec.rb +++ b/spec/features/merge_request/batch_comments_spec.rb @@ -101,7 +101,7 @@ RSpec.describe 'Merge request > Batch comments', :js do write_diff_comment - visit_overview + visit_overview_with_pending_comment end it 'can add comment to review' do @@ -232,6 +232,14 @@ RSpec.describe 'Merge request > Batch comments', :js do wait_for_requests end + def visit_overview_with_pending_comment + accept_alert do + visit project_merge_request_path(merge_request.project, merge_request) + end + + wait_for_requests + end + def write_diff_comment(**params) click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[0][:line_code]}']")) diff --git a/spec/features/merge_request/maintainer_edits_fork_spec.rb b/spec/features/merge_request/maintainer_edits_fork_spec.rb index a98bfd1c8a4..39d948bb6fb 100644 --- a/spec/features/merge_request/maintainer_edits_fork_spec.rb +++ b/spec/features/merge_request/maintainer_edits_fork_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' RSpec.describe 'a maintainer edits files on a source-branch of an MR from a fork', :js, :sidekiq_might_not_need_inline do + include Spec::Support::Helpers::Features::SourceEditorSpecHelpers include ProjectForksHelper let(:user) { create(:user, username: 'the-maintainer') } let(:target_project) { create(:project, :public, :repository) } @@ -40,12 +41,13 @@ RSpec.describe 'a maintainer edits files on a source-branch of an MR from a fork end it 'allows committing to the source branch' do - execute_script("monaco.editor.getModels()[0].setValue('Updated the readme')") + content = 'Updated the readme' + editor_set_value(content) click_button 'Commit changes' wait_for_requests expect(page).to have_content('Your changes have been successfully committed') - expect(page).to have_content('Updated the readme') + expect(page).to have_content(content) end end diff --git a/spec/features/merge_request/user_approves_spec.rb b/spec/features/merge_request/user_approves_spec.rb index 4f7bcb58551..9670012803e 100644 --- a/spec/features/merge_request/user_approves_spec.rb +++ b/spec/features/merge_request/user_approves_spec.rb @@ -27,7 +27,7 @@ RSpec.describe 'Merge request > User approves', :js do def verify_approvals_count_on_index! visit(project_merge_requests_path(project, state: :all)) - expect(page.all('li').any? { |item| item["title"] == "1 approver (you've approved)"}).to be true + expect(page.all('li').any? { |item| item["title"] == "1 approver (you've approved)" }).to be true visit project_merge_request_path(project, merge_request) end diff --git a/spec/features/merge_request/user_comments_on_merge_request_spec.rb b/spec/features/merge_request/user_comments_on_merge_request_spec.rb index 43096f8e7f9..dbcfc2b968f 100644 --- a/spec/features/merge_request/user_comments_on_merge_request_spec.rb +++ b/spec/features/merge_request/user_comments_on_merge_request_spec.rb @@ -51,6 +51,45 @@ RSpec.describe 'User comments on a merge request', :js do expect(page).to have_button('Resolve thread') end + array = [':', '@', '#', '%', '!', '~', '$', '[contact:'] + array.each do |x| + it 'handles esc key correctly when atwho is active' do + page.within('.js-main-target-form') do + fill_in('note[note]', with: 'comment 1') + click_button('Comment') + end + + wait_for_requests + + page.within('.note') do + click_button('Reply to comment') + fill_in('note[note]', with: x) + send_keys :escape + end + + wait_for_requests + expect(page.html).not_to include('Are you sure you want to cancel creating this comment?') + end + end + + it 'handles esc key correctly when atwho is not active' do + page.within('.js-main-target-form') do + fill_in('note[note]', with: 'comment 1') + click_button('Comment') + end + + wait_for_requests + + page.within('.note') do + click_button('Reply to comment') + fill_in('note[note]', with: 'comment 2') + send_keys :escape + end + + wait_for_requests + expect(page.html).to include('Are you sure you want to cancel creating this comment?') + end + it 'loads new comment' do # Add new comment in background in order to check # if it's going to be loaded automatically for current user. diff --git a/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb index 059e1eb89c5..f0c0142a6cc 100644 --- a/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb +++ b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb @@ -5,8 +5,8 @@ require 'spec_helper' RSpec.describe 'Merge request < User customizes merge commit message', :js do let(:project) { create(:project, :public, :repository) } let(:user) { project.creator } - let(:issue_1) { create(:issue, project: project)} - let(:issue_2) { create(:issue, project: project)} + let(:issue_1) { create(:issue, project: project) } + let(:issue_2) { create(:issue, project: project) } let(:source_branch) { 'csv' } let(:target_branch) { 'master' } let(:squash) { false } diff --git a/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb b/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb index 92b9b785148..0dd87ac3e24 100644 --- a/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb +++ b/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb @@ -89,7 +89,7 @@ RSpec.describe 'Merge request > User edits assignees sidebar', :js do context 'when GraphQL assignees widget feature flag is enabled' do let(:sidebar_assignee_dropdown_item) { sidebar_assignee_block.find(".dropdown-item", text: assignee.username ) } - let(:sidebar_assignee_dropdown_tooltip) { sidebar_assignee_dropdown_item['title']} + let(:sidebar_assignee_dropdown_tooltip) { sidebar_assignee_dropdown_item['title'] } context 'when user is an owner' do before do diff --git a/spec/features/merge_request/user_merges_merge_request_spec.rb b/spec/features/merge_request/user_merges_merge_request_spec.rb index 6a9a30953df..c91dc7b1c00 100644 --- a/spec/features/merge_request/user_merges_merge_request_spec.rb +++ b/spec/features/merge_request/user_merges_merge_request_spec.rb @@ -21,27 +21,6 @@ RSpec.describe "User merges a merge request", :js do end end - context "ff-only merge" do - let(:project) { create(:project, :public, :repository, merge_requests_ff_only_enabled: true) } - - before do - stub_feature_flags(restructured_mr_widget: false) - visit(merge_request_path(merge_request)) - end - - context "when branch is rebased" do - let!(:merge_request) { create(:merge_request, :rebased, source_project: project) } - - it_behaves_like "fast forward merge a merge request" - end - - context "when branch is merged" do - let!(:merge_request) { create(:merge_request, :merged_target, source_project: project) } - - it_behaves_like "fast forward merge a merge request" - end - end - context 'sidebar merge requests counter' do let(:project) { create(:project, :public, :repository) } let!(:merge_request) { create(:merge_request, source_project: project) } diff --git a/spec/features/merge_request/user_opens_context_commits_modal_spec.rb b/spec/features/merge_request/user_opens_context_commits_modal_spec.rb new file mode 100644 index 00000000000..2d574e57fe9 --- /dev/null +++ b/spec/features/merge_request/user_opens_context_commits_modal_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Merge request > Context commits', :js do + let(:user) { create(:user) } + let(:project) { create(:project, :public, :repository) } + let(:merge_request) { create(:merge_request, source_project: project) } + + before do + project.add_developer(user) + + sign_in(user) + + visit commits_project_merge_request_path(project, merge_request) + + wait_for_requests + end + + it 'opens modal' do + click_button 'Add previously merged commits' + + expect(page).to have_selector('#add-review-item') + expect(page).to have_content('Add or remove previously merged commits') + end +end diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb index d461170c990..1eebb6c2e28 100644 --- a/spec/features/merge_request/user_posts_diff_notes_spec.rb +++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb @@ -19,7 +19,6 @@ RSpec.describe 'Merge request > User posts diff notes', :js do project.add_developer(user) sign_in(user) - stub_const('Gitlab::QueryLimiting::Transaction::THRESHOLD', 104) end context 'when hovering over a parallel view diff file' do 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 7b7fff5c936..f56db3d3dbe 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 @@ -5,8 +5,8 @@ require 'spec_helper' RSpec.describe 'Merge request > User sees closing issues message', :js do let(:project) { create(:project, :public, :repository) } let(:user) { project.creator } - let(:issue_1) { create(:issue, project: project)} - let(:issue_2) { create(:issue, project: project)} + let(:issue_1) { create(:issue, project: project) } + let(:issue_2) { create(:issue, project: project) } let(:merge_request) do create( :merge_request, diff --git a/spec/features/merge_request/user_sees_deployment_widget_spec.rb b/spec/features/merge_request/user_sees_deployment_widget_spec.rb index e045f11c0d8..c02149eed87 100644 --- a/spec/features/merge_request/user_sees_deployment_widget_spec.rb +++ b/spec/features/merge_request/user_sees_deployment_widget_spec.rb @@ -14,7 +14,7 @@ RSpec.describe 'Merge request > User sees deployment widget', :js do let(:ref) { merge_request.target_branch } let(:sha) { project.commit(ref).id } let(:pipeline) { create(:ci_pipeline, sha: sha, project: project, ref: ref) } - let!(:manual) { } + let!(:manual) {} let(:build) { create(:ci_build, :with_deployment, environment: environment.name, pipeline: pipeline) } let!(:deployment) { build.deployment } diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb index 50f4cce5c23..2e65183d26f 100644 --- a/spec/features/merge_request/user_sees_diff_spec.rb +++ b/spec/features/merge_request/user_sees_diff_spec.rb @@ -86,7 +86,7 @@ RSpec.describe 'Merge request > User sees diff', :js do context 'when file contains html' do let(:current_user) { project.first_owner } - let(:branch_name) {"test_branch"} + let(:branch_name) { "test_branch" } it 'escapes any HTML special characters in the diff chunk header' do file_content = 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 09c6b6bce3b..2a1b9ea6009 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 @@ -25,7 +25,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', } end - let(:expected_detached_mr_tag) {'merge request'} + let(:expected_detached_mr_tag) { 'merge request' } before do stub_application_setting(auto_devops_enabled: false) diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb index 16b1de0393f..11e542916f9 100644 --- a/spec/features/merge_request/user_sees_pipelines_spec.rb +++ b/spec/features/merge_request/user_sees_pipelines_spec.rb @@ -78,9 +78,18 @@ RSpec.describe 'Merge request > User sees pipelines', :js do it 'user visits merge request page' do page.within('.merge-request-tabs') do - expect(page).to have_no_link('Pipelines') + expect(page).to have_link('Pipelines') end end + + it 'shows empty state with run pipeline button' do + page.within('.merge-request-tabs') do + click_link('Pipelines') + end + + expect(page).to have_content('There are currently no pipelines.') + expect(page.find('[data-testid="run_pipeline_button"]')).to have_text('Run pipeline') + end end end diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb index 2c2a2dfd4a8..0e86e970f46 100644 --- a/spec/features/merge_request/user_sees_versions_spec.rb +++ b/spec/features/merge_request/user_sees_versions_spec.rb @@ -232,7 +232,7 @@ RSpec.describe 'Merge request > User sees versions', :js do end it 'only shows diffs from the commit' do - diff_commit_ids = find_all('.diff-file [data-commit-id]').map {|diff| diff['data-commit-id']} + diff_commit_ids = find_all('.diff-file [data-commit-id]').map { |diff| diff['data-commit-id'] } expect(diff_commit_ids).not_to be_empty expect(diff_commit_ids).to all(eq(params[:commit_id])) diff --git a/spec/features/merge_request/user_uses_quick_actions_spec.rb b/spec/features/merge_request/user_uses_quick_actions_spec.rb index b48659353ec..563120fc8b7 100644 --- a/spec/features/merge_request/user_uses_quick_actions_spec.rb +++ b/spec/features/merge_request/user_uses_quick_actions_spec.rb @@ -24,7 +24,7 @@ RSpec.describe 'Merge request > User uses quick actions', :js do let!(:label_feature) { create(:label, project: project, title: 'feature') } let!(:milestone) { create(:milestone, project: project, title: 'ASAP') } let(:issuable) { create(:merge_request, source_project: project) } - let(:source_issuable) { create(:issue, project: project, milestone: milestone, labels: [label_bug, label_feature])} + let(:source_issuable) { create(:issue, project: project, milestone: milestone, labels: [label_bug, label_feature]) } it_behaves_like 'close quick action', :merge_request it_behaves_like 'issuable time tracker', :merge_request diff --git a/spec/features/merge_requests/user_mass_updates_spec.rb b/spec/features/merge_requests/user_mass_updates_spec.rb index fa866beb773..cf9760bcd7f 100644 --- a/spec/features/merge_requests/user_mass_updates_spec.rb +++ b/spec/features/merge_requests/user_mass_updates_spec.rb @@ -9,8 +9,6 @@ RSpec.describe 'Merge requests > User mass updates', :js do let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } before do - stub_feature_flags(mr_attention_requests: false) - project.add_maintainer(user) project.add_maintainer(user2) sign_in(user) @@ -63,18 +61,6 @@ RSpec.describe 'Merge requests > User mass updates', :js do expect(find('.merge-request')).to have_link "Assigned to #{user.name}" end - - describe 'with attention requests feature flag on' do - before do - stub_feature_flags(mr_attention_requests: true) - end - - it 'updates merge request with assignee' do - change_assignee(user2.name) - - expect(find('.issuable-meta a.author-link')[:title]).to eq "Attention requested from assignee #{user2.name}" - end - end end describe 'remove assignee' do diff --git a/spec/features/oauth_registration_spec.rb b/spec/features/oauth_registration_spec.rb index 18dd10755b1..cb8343b8065 100644 --- a/spec/features/oauth_registration_spec.rb +++ b/spec/features/oauth_registration_spec.rb @@ -85,7 +85,46 @@ RSpec.describe 'OAuth Registration', :js, :allow_forgery_protection do expect(page).to have_content('Please complete your profile with email address') end end + + context 'when registering via an invitation email' do + let_it_be(:owner) { create(:user) } + let_it_be(:group) { create(:group, name: 'Owned') } + let_it_be(:project) { create(:project, :repository, namespace: group) } + + let(:invite_email) { generate(:email) } + let(:extra_params) { { invite_type: Emails::Members::INITIAL_INVITE } } + let(:group_invite) do + create( + :group_member, :invited, + group: group, + invite_email: invite_email, + created_by: owner + ) + end + + before do + project.add_maintainer(owner) + group.add_owner(owner) + group_invite.generate_invite_token! + + mock_auth_hash(provider, uid, invite_email, additional_info: additional_info) + end + + it 'redirects to the activity page with all the projects/groups invitations accepted' do + visit invite_path(group_invite.raw_invite_token, extra_params) + click_link_or_button "oauth-login-#{provider}" + fill_in_welcome_form + + expect(page).to have_content('You have been granted Owner access to group Owned.') + expect(page).to have_current_path(activity_group_path(group), ignore_query: true) + end + end end end end + + def fill_in_welcome_form + select 'Software Developer', from: 'user_role' + click_button 'Get started!' + end end diff --git a/spec/features/populate_new_pipeline_vars_with_params_spec.rb b/spec/features/populate_new_pipeline_vars_with_params_spec.rb index 937f99558ad..744543d1252 100644 --- a/spec/features/populate_new_pipeline_vars_with_params_spec.rb +++ b/spec/features/populate_new_pipeline_vars_with_params_spec.rb @@ -16,7 +16,6 @@ RSpec.describe "Populate new pipeline CI variables with url params", :js do it "var[key1]=value1 populates env_var variable correctly" do page.within(all("[data-testid='ci-variable-row']")[0]) do - expect(find("[data-testid='pipeline-form-ci-variable-type']").value).to eq('env_var') expect(find("[data-testid='pipeline-form-ci-variable-key']").value).to eq('key1') expect(find("[data-testid='pipeline-form-ci-variable-value']").value).to eq('value1') end @@ -24,7 +23,6 @@ RSpec.describe "Populate new pipeline CI variables with url params", :js do it "file_var[key2]=value2 populates file variable correctly" do page.within(all("[data-testid='ci-variable-row']")[1]) do - expect(find("[data-testid='pipeline-form-ci-variable-type']").value).to eq('file') expect(find("[data-testid='pipeline-form-ci-variable-key']").value).to eq('key2') expect(find("[data-testid='pipeline-form-ci-variable-value']").value).to eq('value2') end diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index 1013937ebb9..2836ac2f801 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -30,7 +30,7 @@ RSpec.describe 'Profile account page', :js do it 'deletes user', :js, :sidekiq_might_not_need_inline do click_button 'Delete account' - fill_in 'password', with: '12345678' + fill_in 'password', with: user.password page.within '.modal' do click_button 'Delete account' diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb index 07dfbca8cbd..1d0db488751 100644 --- a/spec/features/profiles/password_spec.rb +++ b/spec/features/profiles/password_spec.rb @@ -13,6 +13,7 @@ RSpec.describe 'Profile > Password' do end context 'Password authentication enabled' do + let(:new_password) { User.random_password } let(:user) { create(:user, password_automatically_set: true) } before do @@ -23,7 +24,7 @@ RSpec.describe 'Profile > Password' do context 'User with password automatically set' do describe 'User puts different passwords in the field and in the confirmation' do it 'shows an error message' do - fill_passwords('mypassword', 'mypassword2') + fill_passwords(new_password, "#{new_password}2") page.within('.gl-alert-danger') do expect(page).to have_content("Password confirmation doesn't match Password") @@ -31,7 +32,7 @@ RSpec.describe 'Profile > Password' do end it 'does not contain the current password field after an error' do - fill_passwords('mypassword', 'mypassword2') + fill_passwords(new_password, "#{new_password}2") expect(page).to have_no_field('user[current_password]') end @@ -39,7 +40,7 @@ RSpec.describe 'Profile > Password' do describe 'User puts the same passwords in the field and in the confirmation' do it 'shows a success message' do - fill_passwords('mypassword', 'mypassword') + fill_passwords(new_password, new_password) page.within('[data-testid="alert-info"]') do expect(page).to have_content('Password was successfully updated. Please sign in again.') @@ -79,7 +80,7 @@ RSpec.describe 'Profile > Password' do end context 'Change password' do - let(:new_password) { '22233344' } + let(:new_password) { User.random_password } before do sign_in(user) @@ -156,6 +157,8 @@ RSpec.describe 'Profile > Password' do end context 'when password is expired' do + let(:new_password) { User.random_password } + before do sign_in(user) @@ -170,8 +173,8 @@ RSpec.describe 'Profile > Password' do expect(page).to have_current_path new_profile_password_path, ignore_query: true fill_in :user_password, with: user.password - fill_in :user_new_password, with: '12345678' - fill_in :user_password_confirmation, with: '12345678' + fill_in :user_new_password, with: new_password + fill_in :user_password_confirmation, with: new_password click_button 'Set new password' expect(page).to have_current_path new_user_session_path, ignore_query: true diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb index bca1bc4df4d..088c8a7a15a 100644 --- a/spec/features/profiles/personal_access_tokens_spec.rb +++ b/spec/features/profiles/personal_access_tokens_spec.rb @@ -146,12 +146,6 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do end end - it 'pushes `personal_access_tokens_scoped_to_projects` feature flag to the frontend' do - visit profile_personal_access_tokens_path - - expect(page).to have_pushed_frontend_feature_flags(personalAccessTokensScopedToProjects: true) - end - it "prefills token details" do name = 'My PAT' scopes = 'api,read_user' diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb index 4b6ed458c68..2f7b722f553 100644 --- a/spec/features/profiles/user_edit_profile_spec.rb +++ b/spec/features/profiles/user_edit_profile_spec.rb @@ -294,7 +294,7 @@ RSpec.describe 'User edit profile' do end context 'user menu' do - let(:issue) { create(:issue, project: project)} + let(:issue) { create(:issue, project: project) } let(:project) { create(:project) } def open_modal(button_text) @@ -536,7 +536,7 @@ RSpec.describe 'User edit profile' do end context 'User time preferences', :js do - let(:issue) { create(:issue, project: project)} + let(:issue) { create(:issue, project: project) } let(:project) { create(:project) } before do diff --git a/spec/features/profiles/user_visits_profile_spec.rb b/spec/features/profiles/user_visits_profile_spec.rb index 8b1af283765..7dd2e6aafa3 100644 --- a/spec/features/profiles/user_visits_profile_spec.rb +++ b/spec/features/profiles/user_visits_profile_spec.rb @@ -97,8 +97,8 @@ RSpec.describe 'User visits their profile' do let_it_be(:storage_enforcement_date) { Date.today + 30 } before do - allow_next_found_instance_of(Namespaces::UserNamespace) do |g| - allow(g).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) + allow_next_found_instance_of(Namespaces::UserNamespace) do |user_namespace| + allow(user_namespace).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) end end @@ -115,8 +115,8 @@ RSpec.describe 'User visits their profile' do expect_page_not_to_have_storage_enforcement_banner storage_enforcement_date = Date.today + 13 - allow_next_found_instance_of(Namespaces::UserNamespace) do |g| - allow(g).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) + allow_next_found_instance_of(Namespaces::UserNamespace) do |user_namespace| + allow(user_namespace).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) end page.refresh expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) @@ -124,8 +124,12 @@ RSpec.describe 'User visits their profile' do end context 'with storage_enforcement_date not set' do - # This test should break and be rewritten after the implementation of the storage_enforcement_date - # TBD: https://gitlab.com/gitlab-org/gitlab/-/issues/350632 + before do + allow_next_found_instance_of(Namespaces::UserNamespace) do |user_namespace| + allow(user_namespace).to receive(:storage_enforcement_date).and_return(nil) + end + end + it 'does not display the banner in the group page' do visit(profile_path) expect_page_not_to_have_storage_enforcement_banner @@ -134,10 +138,10 @@ RSpec.describe 'User visits their profile' do end def expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) - expect(page).to have_text "From #{storage_enforcement_date} storage limits will apply to this namespace" + expect(page).to have_text "Effective #{storage_enforcement_date}, namespace storage limits will apply" end def expect_page_not_to_have_storage_enforcement_banner - expect(page).not_to have_text "storage limits will apply to this namespace" + expect(page).not_to have_text "namespace storage limits will apply" end end diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb index f5cafa2b2ec..13a4c1b5912 100644 --- a/spec/features/projects/blobs/blob_show_spec.rb +++ b/spec/features/projects/blobs/blob_show_spec.rb @@ -137,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 # rubocop:disable QA/SelectorUsage + first('[data-testid="branches-select"]').click page.within '.project-refs-form' do click_link ref_name diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb index 54176378de8..f198a1f42e2 100644 --- a/spec/features/projects/blobs/edit_spec.rb +++ b/spec/features/projects/blobs/edit_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' RSpec.describe 'Editing file blob', :js do + include Spec::Support::Helpers::Features::SourceEditorSpecHelpers include TreeHelper include BlobSpecHelpers @@ -42,7 +43,7 @@ RSpec.describe 'Editing file blob', :js do def fill_editor(content: 'class NextFeature\\nend\\n') wait_for_requests - execute_script("monaco.editor.getModels()[0].setValue('#{content}')") + editor_set_value(content) end context 'from MR diff' do @@ -98,10 +99,8 @@ RSpec.describe 'Editing file blob', :js do click_link 'Preview changes' wait_for_requests - old_line_count = page.all('.line_holder.old').size new_line_count = page.all('.line_holder.new').size - expect(old_line_count).to be > 0 expect(new_line_count).to be > 0 end end diff --git a/spec/features/projects/ci/lint_spec.rb b/spec/features/projects/ci/lint_spec.rb index 7f10c6afcd5..608511ae5a5 100644 --- a/spec/features/projects/ci/lint_spec.rb +++ b/spec/features/projects/ci/lint_spec.rb @@ -16,16 +16,13 @@ RSpec.describe 'CI Lint', :js do visit project_ci_lint_path(project) editor_set_value(yaml_content) - - wait_for('YAML content') do - find(content_selector).text.present? - end end describe 'YAML parsing' do shared_examples 'validates the YAML' do before do click_on 'Validate' + scroll_to(page.find('[data-testid="ci-lint-status"]')) end context 'YAML is correct' do diff --git a/spec/features/projects/ci/secure_files_spec.rb b/spec/features/projects/ci/secure_files_spec.rb deleted file mode 100644 index 412330eb5d6..00000000000 --- a/spec/features/projects/ci/secure_files_spec.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Secure Files', :js do - let(:project) { create(:project) } - let(:user) { create(:user) } - - before do - stub_feature_flags(ci_secure_files_read_only: false) - project.add_maintainer(user) - sign_in(user) - end - - it 'user sees the Secure Files list component' do - visit project_ci_secure_files_path(project) - expect(page).to have_content('There are no secure files yet.') - end - - it 'prompts the user to confirm before deleting a file' do - file = create(:ci_secure_file, project: project) - - visit project_ci_secure_files_path(project) - - expect(page).to have_content(file.name) - - find('button.btn-danger').click - - expect(page).to have_content("Delete #{file.name}?") - - click_on('Delete secure file') - - visit project_ci_secure_files_path(project) - - expect(page).not_to have_content(file.name) - end - - it 'displays an uploaded file in the file list' do - visit project_ci_secure_files_path(project) - expect(page).to have_content('There are no secure files yet.') - - page.attach_file('spec/fixtures/ci_secure_files/upload-keystore.jks') do - click_button 'Upload File' - end - - expect(page).to have_content('upload-keystore.jks') - end - - it 'displays an error when a duplicate file upload is attempted' do - create(:ci_secure_file, project: project, name: 'upload-keystore.jks') - visit project_ci_secure_files_path(project) - - expect(page).to have_content('upload-keystore.jks') - - page.attach_file('spec/fixtures/ci_secure_files/upload-keystore.jks') do - click_button 'Upload File' - end - - expect(page).to have_content('A file with this name already exists.') - end -end diff --git a/spec/features/projects/cluster_agents_spec.rb b/spec/features/projects/cluster_agents_spec.rb index 5d931afe4a7..8c557a9c37a 100644 --- a/spec/features/projects/cluster_agents_spec.rb +++ b/spec/features/projects/cluster_agents_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe 'ClusterAgents', :js do - let_it_be(:token) { create(:cluster_agent_token, description: 'feature test token')} + let_it_be(:token) { create(:cluster_agent_token, description: 'feature test token') } let(:agent) { token.agent } let(:project) { agent.project } diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb index 863fdbdadaa..2719316c5dc 100644 --- a/spec/features/projects/commits/user_browses_commits_spec.rb +++ b/spec/features/projects/commits/user_browses_commits_spec.rb @@ -150,7 +150,7 @@ RSpec.describe 'User browses commits' do let(:ref) { project.repository.root_ref } let(:newrev) { project.repository.commit('master').sha } let(:short_newrev) { project.repository.commit('master').short_id } - let(:message) { 'Glob characters'} + let(:message) { 'Glob characters' } before do create_file_in_repo(project, ref, ref, filename, 'Test file', commit_message: message) diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb index bc3ef2af9b0..22b0f344606 100644 --- a/spec/features/projects/compare_spec.rb +++ b/spec/features/projects/compare_spec.rb @@ -113,7 +113,7 @@ RSpec.describe "Compare", :js do click_button('Compare') - page.within('.gl-alert') do + page.within('[data-testid="too-many-changes-alert"]') do expect(page).to have_text("Too many changes to show. To preserve performance only 3 of 3+ files are displayed.") end end diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb index 951b24eafac..a53e8beb555 100644 --- a/spec/features/projects/environments/environment_spec.rb +++ b/spec/features/projects/environments/environment_spec.rb @@ -18,10 +18,10 @@ RSpec.describe 'Environment' do describe 'environment details page' do let!(:environment) { create(:environment, project: project) } - let!(:permissions) { } - let!(:deployment) { } - let!(:action) { } - let!(:cluster) { } + let!(:permissions) {} + let!(:deployment) {} + let!(:action) {} + let!(:cluster) {} context 'with auto-stop' do let!(:environment) { create(:environment, :will_auto_stop, name: 'staging', project: project) } 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 6b1e60db5b1..0ad44f31a52 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(page).to have_current_path("/-/ide/project/#{project.full_path}/edit/master/-/LICENSE", ignore_query: true) - expect(page).to have_selector('.qa-file-templates-bar') # rubocop:disable QA/SelectorUsage + expect(page).to have_selector('[data-testid="file-templates-bar"]') select_template('MIT License') diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb index 53fdd5a15dd..0f3ce5a2bad 100644 --- a/spec/features/projects/files/user_browses_files_spec.rb +++ b/spec/features/projects/files/user_browses_files_spec.rb @@ -348,7 +348,7 @@ RSpec.describe "User browses files", :js do end it "shows raw file content in a new tab" do - new_tab = window_opened_by {click_link 'Open raw'} + new_tab = window_opened_by { click_link 'Open raw' } within_window new_tab do expect(page).to have_content("Test file") @@ -366,7 +366,7 @@ RSpec.describe "User browses files", :js do end it "shows raw file content in a new tab" do - new_tab = window_opened_by {click_link 'Open raw'} + new_tab = window_opened_by { click_link 'Open raw' } within_window new_tab do expect(page).to have_content("*.rbc") diff --git a/spec/features/projects/files/user_creates_files_spec.rb b/spec/features/projects/files/user_creates_files_spec.rb index 7344c91b6dc..a81f31d663e 100644 --- a/spec/features/projects/files/user_creates_files_spec.rb +++ b/spec/features/projects/files/user_creates_files_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' RSpec.describe 'Projects > Files > User creates files', :js do + include Spec::Support::Helpers::Features::SourceEditorSpecHelpers include BlobSpecHelpers let(:fork_message) do @@ -89,8 +90,7 @@ RSpec.describe 'Projects > Files > User creates files', :js do end it 'creates and commit a new file' do - find('#editor') - execute_script("monaco.editor.getModels()[0].setValue('*.rbca')") + editor_set_value('*.rbca') fill_in(:file_name, with: 'not_a_file.md') fill_in(:commit_message, with: 'New commit message', visible: true) click_button('Commit changes') @@ -107,8 +107,7 @@ RSpec.describe 'Projects > Files > User creates files', :js do it 'creates and commit a new file with new lines at the end of file' do set_default_button('edit') - find('#editor') - execute_script('monaco.editor.getModels()[0].setValue("Sample\n\n\n")') + editor_set_value('Sample\n\n\n') fill_in(:file_name, with: 'not_a_file.md') fill_in(:commit_message, with: 'New commit message', visible: true) click_button('Commit changes') @@ -119,8 +118,7 @@ RSpec.describe 'Projects > Files > User creates files', :js do click_link('Edit') - find('#editor') - expect(evaluate_script('monaco.editor.getModels()[0].getValue()')).to eq("Sample\n\n\n") + expect(find('.monaco-editor')).to have_content('Sample\n\n\n') end it 'creates and commit a new file with a directory name' do @@ -128,8 +126,7 @@ RSpec.describe 'Projects > Files > User creates files', :js do expect(page).to have_selector('.file-editor') - find('#editor') - execute_script("monaco.editor.getModels()[0].setValue('*.rbca')") + editor_set_value('*.rbca') fill_in(:commit_message, with: 'New commit message', visible: true) click_button('Commit changes') @@ -143,8 +140,7 @@ RSpec.describe 'Projects > Files > User creates files', :js do it 'creates and commit a new file specifying a new branch' do expect(page).to have_selector('.file-editor') - find('#editor') - execute_script("monaco.editor.getModels()[0].setValue('*.rbca')") + editor_set_value('*.rbca') fill_in(:file_name, with: 'not_a_file.md') fill_in(:commit_message, with: 'New commit message', visible: true) fill_in(:branch_name, with: 'new_branch_name', visible: true) @@ -178,8 +174,7 @@ RSpec.describe 'Projects > Files > User creates files', :js do it 'creates and commit new file in forked project' do expect(page).to have_selector('.file-editor') - find('#editor') - execute_script("monaco.editor.getModels()[0].setValue('*.rbca')") + editor_set_value('*.rbca') fill_in(:file_name, with: 'not_a_file.md') fill_in(:commit_message, with: 'New commit message', visible: true) diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb index 1ac45970828..d7460538be9 100644 --- a/spec/features/projects/files/user_edits_files_spec.rb +++ b/spec/features/projects/files/user_edits_files_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' RSpec.describe 'Projects > Files > User edits files', :js do + include Spec::Support::Helpers::Features::SourceEditorSpecHelpers include ProjectForksHelper include BlobSpecHelpers @@ -50,10 +51,9 @@ RSpec.describe 'Projects > Files > User edits files', :js do click_link_or_button('Edit') find('.file-editor', match: :first) - find('#editor') - set_editor_value('*.rbca') + editor_set_value('*.rbca') - expect(editor_value).to eq('*.rbca') + expect(find('.monaco-editor')).to have_content('*.rbca') end it 'does not show the edit link if a file is binary' do @@ -72,8 +72,7 @@ RSpec.describe 'Projects > Files > User edits files', :js do click_link_or_button('Edit') find('.file-editor', match: :first) - find('#editor') - set_editor_value('*.rbca') + editor_set_value('*.rbca') fill_in(:commit_message, with: 'New commit message', visible: true) click_button('Commit changes') @@ -91,8 +90,7 @@ RSpec.describe 'Projects > Files > User edits files', :js do find('.file-editor', match: :first) - find('#editor') - set_editor_value('*.rbca') + editor_set_value('*.rbca') fill_in(:commit_message, with: 'New commit message', visible: true) fill_in(:branch_name, with: 'new_branch_name', visible: true) click_button('Commit changes') @@ -110,8 +108,7 @@ RSpec.describe 'Projects > Files > User edits files', :js do click_link_or_button('Edit') find('.file-editor', match: :first) - find('#editor') - set_editor_value('*.rbca') + editor_set_value('*.rbca') click_link('Preview changes') expect(page).to have_css('.line_holder.new') @@ -156,10 +153,9 @@ RSpec.describe 'Projects > Files > User edits files', :js do find('.file-editor', match: :first) - find('#editor') - set_editor_value('*.rbca') + editor_set_value('*.rbca') - expect(editor_value).to eq('*.rbca') + expect(find('.monaco-editor')).to have_content('*.rbca') end it 'opens the Web IDE in a forked project', :sidekiq_might_not_need_inline do @@ -187,8 +183,7 @@ RSpec.describe 'Projects > Files > User edits files', :js do find('.file-editor', match: :first) - find('#editor') - set_editor_value('*.rbca') + editor_set_value('*.rbca') fill_in(:commit_message, with: 'New commit message', visible: true) click_button('Commit changes') @@ -216,8 +211,7 @@ RSpec.describe 'Projects > Files > User edits files', :js do expect(page).not_to have_link('Fork') - find('#editor') - set_editor_value('*.rbca') + editor_set_value('*.rbca') fill_in(:commit_message, with: 'Another commit', visible: true) click_button('Commit changes') diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb index 12c5820a69d..ac83de3e765 100644 --- a/spec/features/projects/issuable_templates_spec.rb +++ b/spec/features/projects/issuable_templates_spec.rb @@ -90,6 +90,34 @@ RSpec.describe 'issuable templates', :js do end end + context 'user creates an issue with a default template from the repo' do + let(:template_content) { 'this is the default template' } + + before do + project.repository.create_file( + user, + '.gitlab/issue_templates/default.md', + template_content, + message: 'added default issue template', + branch_name: 'master' + ) + end + + it 'does not overwrite autosaved description' do + visit new_project_issue_path project + wait_for_requests + + assert_template # default template is loaded the first time + + fill_in 'issue_description', with: 'my own description', fill_options: { clear: :backspace } + + visit new_project_issue_path project + wait_for_requests + + assert_template(expected_content: 'my own description') + end + end + context 'user creates a merge request using templates' do let(:template_content) { 'this is a test "feature-proposal" template' } let(:bug_template_content) { 'this is merge request bug template' } diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb index bb44b70bb3a..289ab8cffa5 100644 --- a/spec/features/projects/jobs/user_browses_jobs_spec.rb +++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb @@ -74,6 +74,7 @@ RSpec.describe 'User browses jobs' do wait_for_requests expect(page).to have_selector('.ci-canceled') + expect(page).not_to have_selector('[data-testid="jobs-table-error-alert"]') end end diff --git a/spec/features/projects/members/manage_groups_spec.rb b/spec/features/projects/members/manage_groups_spec.rb index 006fa3b6eff..e86affbbca1 100644 --- a/spec/features/projects/members/manage_groups_spec.rb +++ b/spec/features/projects/members/manage_groups_spec.rb @@ -162,7 +162,7 @@ RSpec.describe 'Project > Members > Manage groups', :js do let_it_be(:user) { maintainer } let_it_be(:group) { parent_group } let_it_be(:group_within_hierarchy) { create(:group, parent: group) } - let_it_be(:project_within_hierarchy) { create(:project, group: group_within_hierarchy)} + let_it_be(:project_within_hierarchy) { create(:project, group: group_within_hierarchy) } let_it_be(:members_page_path) { project_project_members_path(project) } let_it_be(:members_page_path_within_hierarchy) { project_project_members_path(project_within_hierarchy) } end diff --git a/spec/features/projects/members/manage_members_spec.rb b/spec/features/projects/members/manage_members_spec.rb index 8d229530ef5..56eb02607a5 100644 --- a/spec/features/projects/members/manage_members_spec.rb +++ b/spec/features/projects/members/manage_members_spec.rb @@ -12,106 +12,188 @@ RSpec.describe 'Projects > Members > Manage members', :js do let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project, :internal, namespace: group) } + let(:project_owner) { create(:user, name: "ProjectOwner", username: "project_owner") } + let(:project_maintainer) { create(:user, name: "ProjectMaintainer", username: "project_maintainer") } + let(:group_owner) { user1 } + let(:project_developer) { user2 } + before do - sign_in(user1) - group.add_owner(user1) + project.add_maintainer(project_maintainer) + project.add_owner(project_owner) + group.add_owner(group_owner) + + sign_in(group_owner) end it 'show members from project and group', :aggregate_failures do - project.add_developer(user2) + project.add_developer(project_developer) visit_members_page - expect(first_row).to have_content(user1.name) - expect(second_row).to have_content(user2.name) + expect(first_row).to have_content(group_owner.name) + expect(second_row).to have_content(project_developer.name) end it 'show user once if member of both group and project', :aggregate_failures do - project.add_developer(user1) + group.add_reporter(project_maintainer) visit_members_page - expect(first_row).to have_content(user1.name) - expect(second_row).to be_blank + expect(first_row).to have_content(group_owner.name) + expect(second_row).to have_content(project_maintainer.name) + expect(third_row).to have_content(project_owner.name) + expect(all_rows[3]).to be_blank end - it 'update user access level' do - project.add_developer(user2) + context 'update user access level' do + before do + sign_in(current_user) + end + + context 'as maintainer' do + let(:current_user) { project_maintainer } - visit_members_page + it 'can update a non-Owner member' do + project.add_developer(project_developer) - page.within find_member_row(user2) do - click_button('Developer') - click_button('Reporter') + visit_members_page + + page.within find_member_row(project_developer) do + click_button('Developer') + + page.within '.dropdown-menu' do + expect(page).not_to have_button('Owner') + end + + click_button('Reporter') + + expect(page).to have_button('Reporter') + end + end - expect(page).to have_button('Reporter') + it 'cannot update an Owner member' do + visit_members_page + + page.within find_member_row(project_owner) do + expect(page).not_to have_button('Owner') + end + end end - end - context 'when owner' do - it 'uses ProjectMember access_level_roles for the invite members modal access option', :aggregate_failures do - visit_members_page + context 'as owner' do + let(:current_user) { group_owner } - click_on 'Invite members' + it 'can update a project Owner member' do + visit_members_page - click_on 'Guest' - wait_for_requests + page.within find_member_row(project_owner) do + click_button('Owner') + click_button('Reporter') - page.within '.dropdown-menu' do - expect(page).to have_button('Guest') - expect(page).to have_button('Reporter') - expect(page).to have_button('Developer') - expect(page).to have_button('Maintainer') - expect(page).to have_button('Owner') + expect(page).to have_button('Reporter') + end end end end - context 'when maintainer' do - let(:maintainer) { create(:user) } - + context 'uses ProjectMember valid_access_level_roles for the invite members modal options', :aggregate_failures do before do - project.add_maintainer(maintainer) - sign_in(maintainer) - end + sign_in(current_user) - it 'uses ProjectMember access_level_roles for the invite members modal access option', :aggregate_failures do visit_members_page click_on 'Invite members' click_on 'Guest' wait_for_requests + end - page.within '.dropdown-menu' do - expect(page).to have_button('Guest') - expect(page).to have_button('Reporter') - expect(page).to have_button('Developer') - expect(page).to have_button('Maintainer') - expect(page).not_to have_button('Owner') + context 'when owner' do + let(:current_user) { project_owner } + + it 'shows Owner in the dropdown' do + page.within '.dropdown-menu' do + expect(page).to have_button('Guest') + expect(page).to have_button('Reporter') + expect(page).to have_button('Developer') + expect(page).to have_button('Maintainer') + expect(page).to have_button('Owner') + end + end + end + + context 'when maintainer' do + let(:current_user) { project_maintainer } + + it 'does not show the Owner option' do + page.within '.dropdown-menu' do + expect(page).to have_button('Guest') + expect(page).to have_button('Reporter') + expect(page).to have_button('Developer') + expect(page).to have_button('Maintainer') + expect(page).not_to have_button('Owner') + end end end end - it 'remove user from project' do - other_user = create(:user) - project.add_developer(other_user) + describe 'remove user from project' do + before do + project.add_developer(project_developer) - visit_members_page + sign_in(current_user) - # Open modal - page.within find_member_row(other_user) do - click_button 'Remove member' + visit_members_page end - within_modal do - expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests' - click_button('Remove member') + context 'when maintainer' do + let(:current_user) { project_maintainer } + + it 'can only remove non-Owner members' do + page.within find_member_row(project_owner) do + expect(page).not_to have_button('Remove member') + end + + # Open modal + page.within find_member_row(project_developer) do + click_button 'Remove member' + end + + within_modal do + expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests' + click_button('Remove member') + end + + wait_for_requests + + expect(members_table).not_to have_content(project_developer.name) + expect(members_table).to have_content(project_owner.name) + end end - wait_for_requests + context 'when owner' do + let(:current_user) { group_owner } + + it 'can remove any direct member' do + page.within find_member_row(project_owner) do + expect(page).to have_button('Remove member') + end + + # Open modal + page.within find_member_row(project_owner) do + click_button 'Remove member' + end - expect(members_table).not_to have_content(other_user.name) + within_modal do + expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests' + click_button('Remove member') + end + + wait_for_requests + + expect(members_table).not_to have_content(project_owner.name) + end + end end it_behaves_like 'inviting members', 'project-members-page' do @@ -130,7 +212,7 @@ RSpec.describe 'Projects > Members > Manage members', :js do external_project_bot = create(:user, :project_bot, name: '_external_project_bot_') external_project = create(:project, group: external_group) external_project.add_maintainer(external_project_bot) - external_project.add_maintainer(user1) + external_project.add_maintainer(group_owner) visit_members_page @@ -143,8 +225,8 @@ RSpec.describe 'Projects > Members > Manage members', :js do wait_for_requests - expect(page).to have_content(user1.name) - expect(page).to have_content(user2.name) + expect(page).to have_content(group_owner.name) + expect(page).to have_content(project_developer.name) expect(page).not_to have_content(internal_project_bot.name) expect(page).not_to have_content(external_project_bot.name) end @@ -155,7 +237,7 @@ RSpec.describe 'Projects > Members > Manage members', :js do let_it_be(:project) { create(:project, :public) } before do - sign_out(user1) + sign_out(group_owner) end it 'does not show the Invite members button when not signed in' do @@ -192,7 +274,7 @@ RSpec.describe 'Projects > Members > Manage members', :js do end it 'shows 2FA badge to user with "Maintainer" access level' do - project.add_maintainer(user1) + sign_in(project_maintainer) visit_members_page @@ -209,7 +291,7 @@ RSpec.describe 'Projects > Members > Manage members', :js do end it 'does not show 2FA badge to users with access level below "Maintainer"' do - group.add_developer(user1) + group.add_developer(group_owner) visit_members_page diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb index 335ae6794b7..eb52a7821f9 100644 --- a/spec/features/projects/merge_request_button_spec.rb +++ b/spec/features/projects/merge_request_button_spec.rb @@ -11,6 +11,8 @@ RSpec.describe 'Merge Request button' do let(:forked_project) { fork_project(project, user, repository: true) } shared_examples 'Merge request button only shown when allowed' do + let(:extra_mr_params) { {} } + context 'not logged in' do it 'does not show Create merge request button' do visit url @@ -31,11 +33,8 @@ RSpec.describe 'Merge Request button' do href = project_new_merge_request_path( project, merge_request: { - source_project_id: project.id, - source_branch: 'feature', - target_project_id: project.id, - target_branch: 'master' - } + source_branch: 'feature' + }.merge(extra_mr_params) ) visit url @@ -90,11 +89,8 @@ RSpec.describe 'Merge Request button' do href = project_new_merge_request_path( forked_project, merge_request: { - source_project_id: forked_project.id, - source_branch: 'feature', - target_project_id: forked_project.id, - target_branch: 'master' - } + source_branch: 'feature' + }.merge(extra_mr_params) ) visit fork_url @@ -121,6 +117,7 @@ RSpec.describe 'Merge Request button' do it_behaves_like 'Merge request button only shown when allowed' do let(:url) { project_compare_path(project, from: 'master', to: 'feature') } let(:fork_url) { project_compare_path(forked_project, from: 'master', to: 'feature') } + let(:extra_mr_params) { { target_project_id: project.id, target_branch: 'master' } } end it 'shows the correct merge request button when viewing across forks', :js do @@ -128,9 +125,8 @@ RSpec.describe 'Merge Request button' do project.add_developer(user) href = project_new_merge_request_path( - project, + forked_project, merge_request: { - source_project_id: forked_project.id, source_branch: 'feature', target_project_id: project.id, target_branch: 'master' diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index 9d2d1454d77..f45025d079a 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -3,16 +3,44 @@ require 'spec_helper' RSpec.describe 'New project', :js do - include Select2Helper include Spec::Support::Helpers::Features::TopNavSpecHelpers context 'as a user' do - let(:user) { create(:user) } + let_it_be(:user) { create(:user) } before do sign_in(user) end + it 'shows the project description field when it should' do + description_label = 'Project description (optional)' + + visit new_project_path + click_link 'Create blank project' + + page.within('#blank-project-pane') do + expect(page).not_to have_content(description_label) + end + + visit new_project_path + click_link 'Import project' + + page.within('#import-project-pane') do + click_button 'Repository by URL' + + expect(page).to have_content(description_label) + end + + visit new_project_path + click_link 'Create from template' + + page.within('#create-from-template-pane') do + find("[data-testid='use_template_#{Gitlab::ProjectTemplate.localized_templates_table.first.name}']").click + + expect(page).to have_content(description_label) + end + end + it 'shows a message if multiple levels are restricted' do Gitlab::CurrentSettings.update!( restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL] diff --git a/spec/features/projects/pages/user_adds_domain_spec.rb b/spec/features/projects/pages/user_adds_domain_spec.rb index afa3f29ce0d..5cb4fa163c8 100644 --- a/spec/features/projects/pages/user_adds_domain_spec.rb +++ b/spec/features/projects/pages/user_adds_domain_spec.rb @@ -5,7 +5,7 @@ RSpec.describe 'User adds pages domain', :js do include LetsEncryptHelpers include Spec::Support::Helpers::ModalHelpers - let_it_be(:project) { create(:project, pages_https_only: false) } + let_it_be(:project) { create(:project, :pages_published, pages_https_only: false) } let(:user) { create(:user) } @@ -18,8 +18,6 @@ RSpec.describe 'User adds pages domain', :js do end context 'when pages are exposed on external HTTP address', :http_pages_enabled do - let(:project) { create(:project, pages_https_only: false) } - shared_examples 'adds new domain' do it 'adds new domain' do visit new_project_pages_domain_path(project) @@ -42,7 +40,7 @@ RSpec.describe 'User adds pages domain', :js do context 'when project in group namespace' do it_behaves_like 'adds new domain' do let(:group) { create :group } - let(:project) { create(:project, namespace: group, pages_https_only: false) } + let(:project) { create(:project, :pages_published, namespace: group, pages_https_only: false) } end end diff --git a/spec/features/projects/pages/user_configures_pages_pipeline_spec.rb b/spec/features/projects/pages/user_configures_pages_pipeline_spec.rb new file mode 100644 index 00000000000..029479d6b95 --- /dev/null +++ b/spec/features/projects/pages/user_configures_pages_pipeline_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe 'Pages edits pages settings', :js do + include Spec::Support::Helpers::ModalHelpers + + let_it_be(:project) { create(:project, pages_https_only: false) } + let_it_be(:user) { create(:user) } + + before do + allow(Gitlab.config.pages).to receive(:enabled).and_return(true) + + project.add_maintainer(user) + + sign_in(user) + end + + context 'when pipeline wizard feature is enabled' do + before do + Feature.enable(:use_pipeline_wizard_for_pages) + end + + context 'when onboarding is not complete' do + it 'renders onboarding instructions' do + visit project_pages_path(project) + + expect(page).to have_content('Get started with Pages') + end + end + + context 'when onboarding is complete' do + before do + project.mark_pages_onboarding_complete + end + + it 'shows waiting screen' do + visit project_pages_path(project) + + expect(page).to have_content('Waiting for the Pages Pipeline to complete...') + end + end + end + + context 'when pipeline wizard feature is disabled' do + before do + Feature.disable(:use_pipeline_wizard_for_pages) + end + + it 'shows configure pages instructions' do + visit project_pages_path(project) + + expect(page).to have_content('Configure pages') + end + + after do + Feature.enable(:use_pipeline_wizard_for_pages) + end + end +end diff --git a/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb b/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb index 4c633bea64e..2e28fa20b90 100644 --- a/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb +++ b/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb @@ -5,7 +5,8 @@ RSpec.describe "Pages with Let's Encrypt", :https_pages_enabled do include LetsEncryptHelpers include Spec::Support::Helpers::ModalHelpers - let(:project) { create(:project, pages_https_only: false) } + let_it_be_with_reload(:project) { create(:project, :pages_published, pages_https_only: false) } + let(:user) { create(:user) } let(:role) { :maintainer } let(:certificate_pem) { attributes_for(:pages_domain)[:certificate] } diff --git a/spec/features/projects/pages/user_edits_settings_spec.rb b/spec/features/projects/pages/user_edits_settings_spec.rb index bd163f4a109..88c27a6adf2 100644 --- a/spec/features/projects/pages/user_edits_settings_spec.rb +++ b/spec/features/projects/pages/user_edits_settings_spec.rb @@ -4,8 +4,8 @@ require 'spec_helper' RSpec.describe 'Pages edits pages settings', :js do include Spec::Support::Helpers::ModalHelpers - let(:project) { create(:project, pages_https_only: false) } - let(:user) { create(:user) } + let_it_be_with_reload(:project) { create(:project, :pages_published, pages_https_only: false) } + let_it_be(:user) { create(:user) } before do allow(Gitlab.config.pages).to receive(:enabled).and_return(true) @@ -80,13 +80,6 @@ RSpec.describe 'Pages edits pages settings', :js do end end - it 'does not see anything to destroy' do - visit project_pages_path(project) - - expect(page).to have_content('Configure pages') - expect(page).not_to have_link('Remove pages') - end - describe 'project settings page' do it 'renders "Pages" tab' do visit edit_project_path(project) @@ -151,7 +144,7 @@ RSpec.describe 'Pages edits pages settings', :js do end context 'non-HTTPS domain exists' do - let(:project) { create(:project, pages_https_only: false) } + let(:project) { create(:project, :pages_published, pages_https_only: false) } before do create(:pages_domain, :without_key, :without_certificate, project: project) diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb index 8cf6d5bd29b..0711a30e974 100644 --- a/spec/features/projects/pipeline_schedules_spec.rb +++ b/spec/features/projects/pipeline_schedules_spec.rb @@ -109,7 +109,12 @@ RSpec.describe 'Pipeline Schedules', :js do end it 'changes ownership of the pipeline' do - click_link 'Take ownership' + click_button 'Take ownership' + + page.within('#pipeline-take-ownership-modal') do + click_link 'Take ownership' + end + page.within('.pipeline-schedule-table-row') do expect(page).not_to have_content('No owner') expect(page).to have_link('Sidney Jones') diff --git a/spec/features/projects/pipelines/legacy_pipeline_spec.rb b/spec/features/projects/pipelines/legacy_pipeline_spec.rb index db6feecba03..14f60dfe061 100644 --- a/spec/features/projects/pipelines/legacy_pipeline_spec.rb +++ b/spec/features/projects/pipelines/legacy_pipeline_spec.rb @@ -385,6 +385,37 @@ RSpec.describe 'Pipeline', :js do end end + describe 'test tabs' do + let(:pipeline) { create(:ci_pipeline, :with_test_reports, :with_report_results, project: project) } + + before do + stub_feature_flags(pipeline_tabs_vue: false) + visit_pipeline + wait_for_requests + end + + context 'with test reports' do + it 'shows badge counter in Tests tab' do + expect(page.find('.js-test-report-badge-counter').text).to eq(pipeline.test_report_summary.total[:count].to_s) + end + + it 'calls summary.json endpoint', :js do + find('.js-tests-tab-link').click + + expect(page).to have_content('Jobs') + expect(page).to have_selector('[data-testid="tests-detail"]', visible: :all) + end + end + + context 'without test reports' do + let(:pipeline) { create(:ci_pipeline, project: project) } + + it 'shows zero' do + expect(page.find('.js-test-report-badge-counter', visible: :all).text).to eq("0") + end + end + end + context 'retrying jobs' do before do visit_pipeline diff --git a/spec/features/projects/pipelines/legacy_pipelines_spec.rb b/spec/features/projects/pipelines/legacy_pipelines_spec.rb index 15d889933bf..eb8f2de3aba 100644 --- a/spec/features/projects/pipelines/legacy_pipelines_spec.rb +++ b/spec/features/projects/pipelines/legacy_pipelines_spec.rb @@ -7,7 +7,7 @@ RSpec.describe 'Pipelines', :js do include Spec::Support::Helpers::ModalHelpers let(:project) { create(:project) } - let(:expected_detached_mr_tag) {'merge request'} + let(:expected_detached_mr_tag) { 'merge request' } context 'when user is logged in' do let(:user) { create(:user) } @@ -727,6 +727,7 @@ RSpec.describe 'Pipelines', :js do end it { expect(page).to have_content('Missing CI config file') } + it 'creates a pipeline after first request failed and a valid gitlab-ci.yml file' \ 'is available when trying again' do stub_ci_pipeline_to_return_yaml_file diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index a83d4191f38..cfdd851cb80 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -394,7 +394,7 @@ RSpec.describe 'Pipeline', :js do expect(page).to have_selector('button[aria-label="Cancel downstream pipeline"]') end - context 'when canceling' do + context 'when canceling', :sidekiq_inline do before do find('button[aria-label="Cancel downstream pipeline"]').click wait_for_requests @@ -515,18 +515,17 @@ RSpec.describe 'Pipeline', :js do let(:pipeline) { create(:ci_pipeline, :with_test_reports, :with_report_results, project: project) } before do - stub_feature_flags(pipeline_tabs_vue: false) visit_pipeline wait_for_requests end context 'with test reports' do it 'shows badge counter in Tests tab' do - expect(page.find('.js-test-report-badge-counter').text).to eq(pipeline.test_report_summary.total[:count].to_s) + expect(page.find('[data-testid="tests-counter"]').text).to eq(pipeline.test_report_summary.total[:count].to_s) end it 'calls summary.json endpoint', :js do - find('.js-tests-tab-link').click + find('.gl-tab-nav-item', text: 'Tests').click expect(page).to have_content('Jobs') expect(page).to have_selector('[data-testid="tests-detail"]', visible: :all) @@ -537,7 +536,7 @@ RSpec.describe 'Pipeline', :js do let(:pipeline) { create(:ci_pipeline, project: project) } it 'shows zero' do - expect(page.find('.js-test-report-badge-counter', visible: :all).text).to eq("0") + expect(page.find('[data-testid="tests-counter"]', visible: :all).text).to eq("0") end end end diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 785edc69623..bf521971ae0 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -7,7 +7,7 @@ RSpec.describe 'Pipelines', :js do include Spec::Support::Helpers::ModalHelpers let(:project) { create(:project) } - let(:expected_detached_mr_tag) {'merge request'} + let(:expected_detached_mr_tag) { 'merge request' } context 'when user is logged in' do let(:user) { create(:user) } @@ -710,6 +710,7 @@ RSpec.describe 'Pipelines', :js do end it { expect(page).to have_content('Missing CI config file') } + it 'creates a pipeline after first request failed and a valid gitlab-ci.yml file is available when trying again' do stub_ci_pipeline_to_return_yaml_file diff --git a/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb b/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb new file mode 100644 index 00000000000..5a50b3de772 --- /dev/null +++ b/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Project > Settings > Packages & Registries > Container registry tag expiration policy' do + let_it_be(:user) { create(:user) } + let_it_be(:project, reload: true) { create(:project, namespace: user.namespace) } + + let(:container_registry_enabled) { true } + let(:container_registry_enabled_on_project) { ProjectFeature::ENABLED } + + subject { visit cleanup_image_tags_project_settings_packages_and_registries_path(project) } + + before do + project.project_feature.update!(container_registry_access_level: container_registry_enabled_on_project) + project.container_expiration_policy.update!(enabled: true) + + sign_in(user) + stub_container_registry_config(enabled: container_registry_enabled) + end + + context 'as owner', :js do + it 'shows available section' do + subject + + expect(find('.breadcrumbs')).to have_content('Clean up image tags') + end + end + + context 'when registry is disabled' do + let(:container_registry_enabled) { false } + + it 'does not exists' do + subject + + expect(page).to have_gitlab_http_status(:not_found) + end + end + + context 'when container registry is disabled on project' do + let(:container_registry_enabled_on_project) { ProjectFeature::DISABLED } + + it 'does not exists' do + subject + + expect(page).to have_gitlab_http_status(:not_found) + end + end +end diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb index 9468540736f..1fb46c669e7 100644 --- a/spec/features/projects/settings/registry_settings_spec.rb +++ b/spec/features/projects/settings/registry_settings_spec.rb @@ -31,7 +31,6 @@ RSpec.describe 'Project > Settings > Packages & Registries > Container registry subject within '[data-testid="container-expiration-policy-project-settings"]' do - click_button('Expand') select('Every day', from: 'Run cleanup') select('50 tags per image name', from: 'Keep the most recent:') fill_in('Keep tags matching:', with: 'stable') @@ -50,7 +49,6 @@ RSpec.describe 'Project > Settings > Packages & Registries > Container registry subject within '[data-testid="container-expiration-policy-project-settings"]' do - click_button('Expand') fill_in('Remove tags matching:', with: '*-production') submit_button = find('[data-testid="save-button"') @@ -76,7 +74,6 @@ RSpec.describe 'Project > Settings > Packages & Registries > Container registry subject within '[data-testid="container-expiration-policy-project-settings"]' do - click_button('Expand') expect(find('[data-testid="enable-toggle"]')).to have_content('Disabled - Tags will not be automatically deleted.') end end @@ -91,7 +88,6 @@ RSpec.describe 'Project > Settings > Packages & Registries > Container registry subject within '[data-testid="container-expiration-policy-project-settings"]' do - click_button('Expand') expect(find('.gl-alert-title')).to have_content('Cleanup policy for tags is disabled') end end diff --git a/spec/features/projects/settings/service_desk_setting_spec.rb b/spec/features/projects/settings/service_desk_setting_spec.rb index 0df4bd3f0d9..86c5c3d2d8c 100644 --- a/spec/features/projects/settings/service_desk_setting_spec.rb +++ b/spec/features/projects/settings/service_desk_setting_spec.rb @@ -81,7 +81,7 @@ RSpec.describe 'Service Desk Setting', :js, :clean_gitlab_redis_cache do } end - let_it_be_with_reload(:group) { create(:group)} + let_it_be_with_reload(:group) { create(:group) } let_it_be_with_reload(:project) { create(:project, :custom_repo, group: group, files: issuable_project_template_files) } let_it_be(:group_template_repo) { create(:project, :custom_repo, group: group, files: issuable_group_template_files) } diff --git a/spec/features/projects/tags/user_edits_tags_spec.rb b/spec/features/projects/tags/user_edits_tags_spec.rb index c8438b73dc3..857d0696659 100644 --- a/spec/features/projects/tags/user_edits_tags_spec.rb +++ b/spec/features/projects/tags/user_edits_tags_spec.rb @@ -15,6 +15,13 @@ RSpec.describe 'Project > Tags', :js do end shared_examples "can create and update release" do + it 'shows tag information' do + visit page_url + + expect(page).to have_content 'v1.1.0' + expect(page).to have_content 'Version 1.1.0' + end + it 'can create new release' do visit page_url page.find("a[href=\"#{new_project_release_path(project, tag_name: 'v1.1.0')}\"]").click @@ -52,71 +59,4 @@ RSpec.describe 'Project > Tags', :js do include_examples "can create and update release" end - - # TODO: remove most of these together with FF https://gitlab.com/gitlab-org/gitlab/-/issues/366244 - describe 'when opening project tags' do - before do - stub_feature_flags(edit_tag_release_notes_via_release_page: false) - visit project_tags_path(project) - end - - context 'page with tags list' do - it 'shows tag name' do - expect(page).to have_content 'v1.1.0' - expect(page).to have_content 'Version 1.1.0' - end - - it 'shows tag edit button' do - page.within '.tags > .content-list' do - edit_btn = page.find("li > .row-fixed-content.controls a.btn-edit[href='/#{project.full_path}/-/tags/v1.1.0/release/edit']") - - expect(edit_btn['href']).to end_with("/#{project.full_path}/-/tags/v1.1.0/release/edit") - end - end - end - - context 'edit tag release notes' do - before do - page.find("li > .row-fixed-content.controls a.btn-edit[href='/#{project.full_path}/-/tags/v1.1.0/release/edit']").click - end - - it 'shows tag name header' do - page.within('.content') do - expect(page.find('.sub-header-block')).to have_content 'Release notes for tag v1.1.0' - end - end - - it 'shows release notes form' do - page.within('.content') do - expect(page).to have_selector('form.release-form') - end - end - - it 'toolbar buttons on release notes form are functional' do - page.within('.content form.release-form') do - note_textarea = page.find('.js-gfm-input') - - # Click on Bold button - page.find('.md-header-toolbar button:first-child').click - - expect(note_textarea.value).to eq('****') - end - end - - it 'release notes form shows "Attach a file" button', :js do - page.within('.content form.release-form') do - expect(page).to have_button('Attach a file') - expect(page).not_to have_selector('.uploading-progress-container', visible: true) - end - end - - it 'shows "Attaching a file" message on uploading 1 file', :js, :capybara_ignore_server_errors do - slow_requests do - dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false) - - expect(page).to have_selector('.attaching-file-message', visible: true, text: 'Attaching a file -') - end - end - end - end end diff --git a/spec/features/projects/tags/user_views_tag_spec.rb b/spec/features/projects/tags/user_views_tag_spec.rb new file mode 100644 index 00000000000..3978c5b7b78 --- /dev/null +++ b/spec/features/projects/tags/user_views_tag_spec.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe 'User views tag', :feature do + include_examples 'user views tag' do + let(:tag_page) { project_tag_path(project, id: tag_name) } + end +end diff --git a/spec/features/projects/tags/user_views_tags_spec.rb b/spec/features/projects/tags/user_views_tags_spec.rb index dfb5d5d9221..d3849df023e 100644 --- a/spec/features/projects/tags/user_views_tags_spec.rb +++ b/spec/features/projects/tags/user_views_tags_spec.rb @@ -2,34 +2,8 @@ require 'spec_helper' RSpec.describe 'User views tags', :feature do - context 'with html' do - let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } - let(:user) { create(:user) } - let(:tag_name) { "stable" } - let!(:release) { create(:release, project: project, tag: tag_name) } - - before do - project.add_developer(user) - project.repository.add_tag(user, tag_name, project.default_branch_or_main) - - sign_in(user) - end - - shared_examples 'renders the tag index page' do - it do - visit project_tags_path(project) - - expect(page).to have_content tag_name - end - end - - it_behaves_like 'renders the tag index page' - - context 'when tag name contains a slash' do - let(:tag_name) { "stable/v0.1" } - - it_behaves_like 'renders the tag index page' - end + include_examples 'user views tag' do + let(:tag_page) { project_tags_path(project) } end context 'rss' do diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb index f6127b38bd6..074469a9b55 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') # rubocop:disable QA/SelectorUsage - find('.qa-begin-commit-button').click # rubocop:disable QA/SelectorUsage + if page.has_css?('[data-testid="begin-commit-button"]') + find('[data-testid="begin-commit-button"]').click 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 33be02a9121..85c644fa528 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') # rubocop:disable QA/SelectorUsage - find('.qa-begin-commit-button').click # rubocop:disable QA/SelectorUsage + if page.has_css?('[data-testid="begin-commit-button"]') + find('[data-testid="begin-commit-button"]').click 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 53e89cd2959..163e347d03d 100644 --- a/spec/features/projects/tree/tree_show_spec.rb +++ b/spec/features/projects/tree/tree_show_spec.rb @@ -54,7 +54,7 @@ RSpec.describe 'Projects tree', :js do let(:filename) { File.join(path, 'test.txt') } let(:newrev) { project.repository.commit('master').sha } let(:short_newrev) { project.repository.commit('master').short_id } - let(:message) { 'Glob characters'} + let(:message) { 'Glob characters' } before do create_file_in_repo(project, 'master', 'master', filename, 'Test file', commit_message: message) diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index f6f9c7f0d3c..d228fb084c3 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -329,7 +329,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') # rubocop:disable QA/SelectorUsage + expect(page).to have_selector('[data-testid="branches-select"]', text: '645f6c4c82fd3f5e06f67134450a570b795e55a6') end context 'for signed commit on default branch', :js do @@ -454,8 +454,8 @@ RSpec.describe 'Project' do let_it_be(:storage_enforcement_date) { Date.today + 30 } before do - allow_next_found_instance_of(Group) do |grp| - allow(grp).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) + allow_next_found_instance_of(Group) do |group| + allow(group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) end end @@ -478,8 +478,8 @@ RSpec.describe 'Project' do let_it_be(:project) { create(:project, namespace: user.namespace) } before do - allow_next_found_instance_of(Namespaces::UserNamespace) do |namspace| - allow(namspace).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) + allow_next_found_instance_of(Namespaces::UserNamespace) do |user_namespace| + allow(user_namespace).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) end end @@ -490,8 +490,8 @@ RSpec.describe 'Project' do end it 'does not display the banner in a paid group project page' do - allow_next_found_instance_of(Group) do |grp| - allow(grp).to receive(:paid?).and_return(true) + allow_next_found_instance_of(Group) do |group| + allow(group).to receive(:paid?).and_return(true) end visit project_path(project) expect_page_not_to_have_storage_enforcement_banner @@ -506,8 +506,8 @@ RSpec.describe 'Project' do expect_page_not_to_have_storage_enforcement_banner storage_enforcement_date = Date.today + 13 - allow_next_found_instance_of(Group) do |grp| - allow(grp).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) + allow_next_found_instance_of(Group) do |group| + allow(group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) end page.refresh expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) @@ -515,8 +515,12 @@ RSpec.describe 'Project' do end context 'with storage_enforcement_date not set' do - # This test should break and be rewritten after the implementation of the storage_enforcement_date - # TBD: https://gitlab.com/gitlab-org/gitlab/-/issues/350632 + before do + allow_next_found_instance_of(Group) do |group| + allow(group).to receive(:storage_enforcement_date).and_return(nil) + end + end + it 'does not display the banner in the group page' do stub_feature_flags(namespace_storage_limit_bypass_date_check: false) visit project_path(project) @@ -526,11 +530,11 @@ RSpec.describe 'Project' do end def expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) - expect(page).to have_text "From #{storage_enforcement_date} storage limits will apply to this namespace" + expect(page).to have_text "Effective #{storage_enforcement_date}, namespace storage limits will apply" end def expect_page_not_to_have_storage_enforcement_banner - expect(page).not_to have_text "storage limits will apply to this namespace" + expect(page).not_to have_text "namespace storage limits will apply" end def remove_with_confirm(button_text, confirm_with, confirm_button_text = 'Confirm') diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 534da71e39a..2600c00346e 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -268,7 +268,7 @@ RSpec.describe 'Runners' do it 'group runners are not available' do visit project_runners_path(project) - expect(page).not_to have_content 'Group owners can register group runners in the group\'s CI/CD settings.' + expect(page).not_to have_content 'To register them, go to the group\'s Runners page.' expect(page).to have_content 'Ask your group owner to set up a group runner' end end @@ -287,7 +287,7 @@ RSpec.describe 'Runners' do expect(page).to have_content 'This group does not have any group runners yet.' - expect(page).to have_content 'Group owners can register group runners in the group\'s CI/CD settings.' + expect(page).to have_content 'To register them, go to the group\'s Runners page.' expect(page).not_to have_content 'Ask your group owner to set up a group runner' end end @@ -313,7 +313,7 @@ RSpec.describe 'Runners' do expect(page).to have_content 'This group does not have any group runners yet.' - expect(page).not_to have_content 'Group owners can register group runners in the group\'s CI/CD settings.' + expect(page).not_to have_content 'To register them, go to the group\'s Runners page.' expect(page).to have_content 'Ask your group owner to set up a group runner.' end end diff --git a/spec/features/search/user_searches_for_commits_spec.rb b/spec/features/search/user_searches_for_commits_spec.rb index 279db686aa9..2dceda09d7c 100644 --- a/spec/features/search/user_searches_for_commits_spec.rb +++ b/spec/features/search/user_searches_for_commits_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe 'User searches for commits', :js do + include CycleAnalyticsHelpers + let(:project) { create(:project, :repository) } let(:sha) { '6d394385cf567f80a8fd85055db1ab4c5295806f' } let(:user) { create(:user) } diff --git a/spec/features/signed_commits_spec.rb b/spec/features/signed_commits_spec.rb index dbf35567803..8725dbcafe8 100644 --- a/spec/features/signed_commits_spec.rb +++ b/spec/features/signed_commits_spec.rb @@ -93,7 +93,7 @@ RSpec.describe 'GPG signed commits' do page.find('.gpg-status-box', text: 'Unverified').click within '.popover' do - expect(page).to have_content 'This commit was signed with a verified signature, but the committer email is not verified to belong to the same user.' + expect(page).to have_content 'This commit was signed with a verified signature, but the committer email is not associated with the GPG Key.' expect(page).to have_content 'Bette Cartwright' expect(page).to have_content '@bette.cartwright' expect(page).to have_content "GPG Key ID: #{GpgHelpers::User2.primary_keyid}" diff --git a/spec/features/tags/developer_updates_tag_spec.rb b/spec/features/tags/developer_updates_tag_spec.rb deleted file mode 100644 index 531ed91c057..00000000000 --- a/spec/features/tags/developer_updates_tag_spec.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -# TODO: remove this file together with FF https://gitlab.com/gitlab-org/gitlab/-/issues/366244 -RSpec.describe 'Developer updates tag' do - let(:user) { create(:user) } - let(:group) { create(:group) } - let(:project) { create(:project, :repository, namespace: group) } - - before do - project.add_developer(user) - sign_in(user) - stub_feature_flags(edit_tag_release_notes_via_release_page: false) - visit project_tags_path(project) - end - - context 'from the tags list page' do - it 'updates the release notes' do - find("li > .row-fixed-content.controls a.btn-edit[href='/#{project.full_path}/-/tags/v1.1.0/release/edit']").click - - fill_in 'release_description', with: 'Awesome release notes' - click_button 'Save changes' - - expect(page).to have_current_path( - project_tag_path(project, 'v1.1.0'), ignore_query: true) - expect(page).to have_content 'v1.1.0' - expect(page).to have_content 'Awesome release notes' - end - - it 'description has emoji autocomplete', :js do - page.within(first('.content-list .controls')) do - click_link 'Edit release notes' - end - - find('#release_description').native.send_keys('') - fill_in 'release_description', with: ':' - - expect(page).to have_selector('.atwho-view') - end - end - - context 'from a specific tag page' do - it 'updates the release notes' do - click_on 'v1.1.0' - click_link 'Edit release notes' - fill_in 'release_description', with: 'Awesome release notes' - click_button 'Save changes' - - expect(page).to have_current_path( - project_tag_path(project, 'v1.1.0'), ignore_query: true) - expect(page).to have_content 'v1.1.0' - expect(page).to have_content 'Awesome release notes' - end - end -end diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb index 6907701de9c..07de3789c08 100644 --- a/spec/features/task_lists_spec.rb +++ b/spec/features/task_lists_spec.rb @@ -89,7 +89,7 @@ RSpec.describe 'Task Lists', :js do it 'provides a summary on Issues#index' do visit project_issues_path(project) - expect(page).to have_content("2 of 6 tasks completed") + expect(page).to have_content("2 of 6 checklist items completed") end end @@ -108,7 +108,7 @@ RSpec.describe 'Task Lists', :js do it 'provides a summary on Issues#index' do visit project_issues_path(project) - expect(page).to have_content("0 of 1 task completed") + expect(page).to have_content("0 of 1 checklist item completed") end end @@ -127,7 +127,7 @@ RSpec.describe 'Task Lists', :js do it 'provides a summary on Issues#index' do visit project_issues_path(project) - expect(page).to have_content("1 of 1 task completed") + expect(page).to have_content("1 of 1 checklist item completed") end end end @@ -253,7 +253,7 @@ RSpec.describe 'Task Lists', :js do it 'provides a summary on MergeRequests#index' do visit project_merge_requests_path(project) - expect(page).to have_content("2 of 6 tasks completed") + expect(page).to have_content("2 of 6 checklist items completed") end end @@ -278,7 +278,7 @@ RSpec.describe 'Task Lists', :js do it 'provides a summary on MergeRequests#index' do visit project_merge_requests_path(project) - expect(page).to have_content("0 of 1 task completed") + expect(page).to have_content("0 of 1 checklist item completed") end end @@ -297,7 +297,7 @@ RSpec.describe 'Task Lists', :js do it 'provides a summary on MergeRequests#index' do visit project_merge_requests_path(project) - expect(page).to have_content("1 of 1 task completed") + expect(page).to have_content("1 of 1 checklist item completed") end end end diff --git a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb index 900cd72c17f..cbd2d30d726 100644 --- a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb +++ b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb @@ -17,7 +17,7 @@ RSpec.describe 'User uploads avatar to profile' do visit user_path(user) - expect(page).to have_selector(%Q(img[data-src$="/uploads/-/system/user/avatar/#{user.id}/dk.png?width=90"])) + expect(page).to have_selector(%Q(img[src$="/uploads/-/system/user/avatar/#{user.id}/dk.png?width=96"])) # Cheating here to verify something that isn't user-facing, but is important expect(user.reload.avatar.file).to exist diff --git a/spec/features/uploads/user_uploads_file_to_note_spec.rb b/spec/features/uploads/user_uploads_file_to_note_spec.rb index 589cc9f9b02..2547e2d274c 100644 --- a/spec/features/uploads/user_uploads_file_to_note_spec.rb +++ b/spec/features/uploads/user_uploads_file_to_note_spec.rb @@ -16,8 +16,8 @@ RSpec.describe 'User uploads file to note' do end context 'before uploading' do - it 'shows "Attach a file" button', :js do - expect(page).to have_button('Attach a file') + it 'shows "Attach a file or image" button', :js do + expect(page).to have_selector('[data-testid="button-attach-file"]') expect(page).not_to have_selector('.uploading-progress-container', visible: true) end end @@ -30,7 +30,7 @@ RSpec.describe 'User uploads file to note' do click_button 'Cancel' end - expect(page).to have_button('Attach a file') + expect(page).to have_selector('[data-testid="button-attach-file"]') expect(page).not_to have_button('Cancel') expect(page).not_to have_selector('.uploading-progress-container', visible: true) end @@ -60,16 +60,15 @@ RSpec.describe 'User uploads file to note' do expect(page).to have_selector('.uploading-error-message', visible: true, text: error_text) expect(page).to have_button('Try again', visible: true) expect(page).to have_button('attach a new file', visible: true) - expect(page).not_to have_button('Attach a file') end end context 'uploading is complete' do - it 'shows "Attach a file" button on uploading complete', :js do + it 'shows "Attach a file or image" button on uploading complete', :js do dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')]) wait_for_requests - expect(page).to have_button('Attach a file') + expect(page).to have_selector('[data-testid="button-attach-file"]') expect(page).not_to have_selector('.uploading-progress-container', visible: true) end diff --git a/spec/features/users/email_verification_on_login_spec.rb b/spec/features/users/email_verification_on_login_spec.rb index 0833f7f6f8e..c8301c2fc91 100644 --- a/spec/features/users/email_verification_on_login_spec.rb +++ b/spec/features/users/email_verification_on_login_spec.rb @@ -335,7 +335,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting mail = find_email_for(user) expect(mail.to).to match_array([user.email]) expect(mail.subject).to eq('Verify your identity') - code = mail.body.parts.first.to_s[/\d{#{VerifiesWithEmail::TOKEN_LENGTH}}/] + code = mail.body.parts.first.to_s[/\d{#{VerifiesWithEmail::TOKEN_LENGTH}}/o] reset_delivered_emails! code end diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb index 3ba3650b608..b875dbe1340 100644 --- a/spec/features/users/login_spec.rb +++ b/spec/features/users/login_spec.rb @@ -49,15 +49,15 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do expect(page).to have_current_path edit_user_password_path, ignore_query: true expect(page).to have_content('Please create a password for your new account.') - fill_in 'user_password', with: 'password' - fill_in 'user_password_confirmation', with: 'password' + fill_in 'user_password', with: user.password + fill_in 'user_password_confirmation', with: user.password click_button 'Change your password' expect(page).to have_current_path new_user_session_path, ignore_query: true expect(page).to have_content(I18n.t('devise.passwords.updated_not_active')) fill_in 'user_login', with: user.username - fill_in 'user_password', with: 'password' + fill_in 'user_password', with: user.password click_button 'Sign in' expect_single_session_with_authenticated_ttl @@ -216,7 +216,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do before do gitlab_sign_in(user, remember: true) - expect(page).to have_content('Two-Factor Authentication') + expect(page).to have_content('Two-factor authentication code') end it 'does not show a "You are already signed in." error message' do @@ -231,7 +231,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do end it 'does not allow sign-in if the user password is updated before entering a one-time code' do - user.update!(password: 'new_password') + user.update!(password: User.random_password) enter_code(user.current_otp) @@ -365,7 +365,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do end context 'when logging in via OAuth' do - let(:user) { create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: 'saml')} + let(:user) { create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: 'saml') } let(:mock_saml_response) do File.read('spec/fixtures/authentication/saml_response.xml') end @@ -407,7 +407,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do sign_in_using_saml! - expect(page).to have_content('Two-Factor Authentication') + expect(page).to have_content('Two-factor authentication code') enter_code(user.current_otp) @@ -468,7 +468,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do visit new_user_session_path fill_in 'user_login', with: user.email - fill_in 'user_password', with: '12345678' + fill_in 'user_password', with: user.password click_button 'Sign in' expect(page).to have_current_path(new_profile_password_path, ignore_query: true) @@ -477,14 +477,14 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do end context 'with invalid username and password' do - let(:user) { create(:user, password: 'not-the-default') } + let(:user) { create(:user) } it 'blocks invalid login' do expect(authentication_metrics) .to increment(:user_unauthenticated_counter) .and increment(:user_password_invalid_counter) - gitlab_sign_in(user) + gitlab_sign_in(user, password: 'incorrect-password') expect_single_session_with_short_ttl expect(page).to have_content('Invalid login or password.') @@ -788,7 +788,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do visit new_user_session_path fill_in 'user_login', with: user.email - fill_in 'user_password', with: '12345678' + fill_in 'user_password', with: user.password click_button 'Sign in' @@ -809,7 +809,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do visit new_user_session_path fill_in 'user_login', with: user.email - fill_in 'user_password', with: '12345678' + fill_in 'user_password', with: user.password click_button 'Sign in' @@ -830,7 +830,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do visit new_user_session_path fill_in 'user_login', with: user.email - fill_in 'user_password', with: '12345678' + fill_in 'user_password', with: user.password click_button 'Sign in' @@ -873,7 +873,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do visit new_user_session_path fill_in 'user_login', with: user.email - fill_in 'user_password', with: '12345678' + fill_in 'user_password', with: user.password click_button 'Sign in' fill_in 'user_otp_attempt', with: user.reload.current_otp @@ -899,7 +899,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do visit new_user_session_path fill_in 'user_login', with: user.email - fill_in 'user_password', with: '12345678' + fill_in 'user_password', with: user.password click_button 'Sign in' expect_to_be_on_terms_page @@ -907,9 +907,11 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do expect(page).to have_current_path(new_profile_password_path, ignore_query: true) - fill_in 'user_password', with: '12345678' - fill_in 'user_new_password', with: 'new password' - fill_in 'user_password_confirmation', with: 'new password' + new_password = User.random_password + + fill_in 'user_password', with: user.password + fill_in 'user_new_password', with: new_password + fill_in 'user_password_confirmation', with: new_password click_button 'Set new password' expect(page).to have_content('Password successfully changed') diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb index 2a444dad486..068e1fd4243 100644 --- a/spec/features/users/show_spec.rb +++ b/spec/features/users/show_spec.rb @@ -132,10 +132,10 @@ RSpec.describe 'User page' do let_it_be(:followee) { create(:user) } let_it_be(:follower) { create(:user) } - it 'does not show link to follow' do + it 'does not show button to follow' do subject - expect(page).not_to have_link(text: 'Follow', class: 'gl-button') + expect(page).not_to have_button(text: 'Follow', class: 'gl-button') end it 'shows 0 followers and 0 following' do @@ -155,11 +155,11 @@ RSpec.describe 'User page' do expect(page).to have_content('1 following') end - it 'does show link to follow' do + it 'does show button to follow' do sign_in(user) visit user_path(followee) - expect(page).to have_link(text: 'Follow', class: 'gl-button') + expect(page).to have_button(text: 'Follow', class: 'gl-button') end it 'does show link to unfollow' do @@ -168,7 +168,7 @@ RSpec.describe 'User page' do visit user_path(followee) - expect(page).to have_link(text: 'Unfollow', class: 'gl-button') + expect(page).to have_button(text: 'Unfollow', class: 'gl-button') end end end diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb index 30441dac7b6..f2381e41de8 100644 --- a/spec/features/users/signup_spec.rb +++ b/spec/features/users/signup_spec.rb @@ -3,39 +3,43 @@ require 'spec_helper' RSpec.shared_examples 'Signup name validation' do |field, max_length, label| - before do - visit new_user_registration_path - end + flag_values = [true, false] + flag_values.each do |val| + before do + stub_feature_flags(restyle_login_page: val) + visit new_user_registration_path + end - describe "#{field} validation", :js do - it "does not show an error border if the user's fullname length is not longer than #{max_length} characters" do - fill_in field, with: 'u' * max_length + describe "#{field} validation", :js do + it "does not show an error border if the user's fullname length is not longer than #{max_length} characters" do + fill_in field, with: 'u' * max_length - expect(find('.name')).not_to have_css '.gl-field-error-outline' - end + expect(find('.name')).not_to have_css '.gl-field-error-outline' + end - it 'shows an error border if the user\'s fullname contains an emoji' do - simulate_input("##{field}", 'Ehsan 🦋') + it 'shows an error border if the user\'s fullname contains an emoji' do + simulate_input("##{field}", 'Ehsan 🦋') - expect(find('.name')).to have_css '.gl-field-error-outline' - end + expect(find('.name')).to have_css '.gl-field-error-outline' + end - it "shows an error border if the user\'s fullname is longer than #{max_length} characters" do - fill_in field, with: 'n' * (max_length + 1) + it "shows an error border if the user\'s fullname is longer than #{max_length} characters" do + fill_in field, with: 'n' * (max_length + 1) - expect(find('.name')).to have_css '.gl-field-error-outline' - end + expect(find('.name')).to have_css '.gl-field-error-outline' + end - it "shows an error message if the user\'s #{label} is longer than #{max_length} characters" do - fill_in field, with: 'n' * (max_length + 1) + it "shows an error message if the user\'s #{label} is longer than #{max_length} characters" do + fill_in field, with: 'n' * (max_length + 1) - expect(page).to have_content("#{label} is too long (maximum is #{max_length} characters).") - end + expect(page).to have_content("#{label} is too long (maximum is #{max_length} characters).") + end - it 'shows an error message if the username contains emojis' do - simulate_input("##{field}", 'Ehsan 🦋') + it 'shows an error message if the username contains emojis' do + simulate_input("##{field}", 'Ehsan 🦋') - expect(page).to have_content("Invalid input, please avoid emojis") + expect(page).to have_content("Invalid input, please avoid emojis") + end end end end @@ -43,10 +47,6 @@ end RSpec.describe 'Signup' do include TermsHelper - before do - stub_application_setting(require_admin_approval_after_user_signup: false) - end - let(:new_user) { build_stubbed(:user) } def fill_in_signup_form @@ -63,214 +63,309 @@ RSpec.describe 'Signup' do visit user_confirmation_path(confirmation_token: new_user_token) end - describe 'username validation', :js do + flag_values = [true, false] + flag_values.each do |val| before do - visit new_user_registration_path + stub_feature_flags(restyle_login_page: val) + stub_application_setting(require_admin_approval_after_user_signup: false) end - it 'does not show an error border if the username is available' do - fill_in 'new_user_username', with: 'new-user' - wait_for_requests + describe 'username validation', :js do + before do + visit new_user_registration_path + end - expect(find('.username')).not_to have_css '.gl-field-error-outline' - end + it 'does not show an error border if the username is available' do + fill_in 'new_user_username', with: 'new-user' + wait_for_requests - it 'does not show an error border if the username contains dots (.)' do - simulate_input('#new_user_username', 'new.user.username') - wait_for_requests + expect(find('.username')).not_to have_css '.gl-field-error-outline' + end - expect(find('.username')).not_to have_css '.gl-field-error-outline' - end + it 'does not show an error border if the username contains dots (.)' do + simulate_input('#new_user_username', 'new.user.username') + wait_for_requests - it 'does not show an error border if the username length is not longer than 255 characters' do - fill_in 'new_user_username', with: 'u' * 255 - wait_for_requests + expect(find('.username')).not_to have_css '.gl-field-error-outline' + end - expect(find('.username')).not_to have_css '.gl-field-error-outline' - end + it 'does not show an error border if the username length is not longer than 255 characters' do + fill_in 'new_user_username', with: 'u' * 255 + wait_for_requests - it 'shows an error border if the username already exists' do - existing_user = create(:user) + expect(find('.username')).not_to have_css '.gl-field-error-outline' + end - fill_in 'new_user_username', with: existing_user.username - wait_for_requests + it 'shows an error border if the username already exists' do + existing_user = create(:user) - expect(find('.username')).to have_css '.gl-field-error-outline' - end + fill_in 'new_user_username', with: existing_user.username + wait_for_requests - it 'shows a success border if the username is available' do - fill_in 'new_user_username', with: 'new-user' - wait_for_requests + expect(find('.username')).to have_css '.gl-field-error-outline' + end - expect(find('.username')).to have_css '.gl-field-success-outline' - end + it 'shows a success border if the username is available' do + fill_in 'new_user_username', with: 'new-user' + wait_for_requests - it 'shows an error border if the username contains special characters' do - fill_in 'new_user_username', with: 'new$user!username' - wait_for_requests + expect(find('.username')).to have_css '.gl-field-success-outline' + end - expect(find('.username')).to have_css '.gl-field-error-outline' - end + it 'shows an error border if the username contains special characters' do + fill_in 'new_user_username', with: 'new$user!username' + wait_for_requests - it 'shows an error border if the username is longer than 255 characters' do - fill_in 'new_user_username', with: 'u' * 256 - wait_for_requests + expect(find('.username')).to have_css '.gl-field-error-outline' + end - expect(find('.username')).to have_css '.gl-field-error-outline' - end + it 'shows an error border if the username is longer than 255 characters' do + fill_in 'new_user_username', with: 'u' * 256 + wait_for_requests - it 'shows an error message if the username is longer than 255 characters' do - fill_in 'new_user_username', with: 'u' * 256 - wait_for_requests + expect(find('.username')).to have_css '.gl-field-error-outline' + end - expect(page).to have_content("Username is too long (maximum is 255 characters).") - end + it 'shows an error message if the username is longer than 255 characters' do + fill_in 'new_user_username', with: 'u' * 256 + wait_for_requests - it 'shows an error message if the username is less than 2 characters' do - fill_in 'new_user_username', with: 'u' - wait_for_requests + expect(page).to have_content("Username is too long (maximum is 255 characters).") + end - expect(page).to have_content("Username is too short (minimum is 2 characters).") - end + it 'shows an error message if the username is less than 2 characters' do + fill_in 'new_user_username', with: 'u' + wait_for_requests - it 'shows an error message on submit if the username contains special characters' do - fill_in 'new_user_username', with: 'new$user!username' - wait_for_requests + expect(page).to have_content("Username is too short (minimum is 2 characters).") + end - click_button "Register" + it 'shows an error message on submit if the username contains special characters' do + fill_in 'new_user_username', with: 'new$user!username' + wait_for_requests - expect(page).to have_content("Please create a username with only alphanumeric characters.") - end + click_button "Register" - it 'shows an error border if the username contains emojis' do - simulate_input('#new_user_username', 'ehsan😀') + expect(page).to have_content("Please create a username with only alphanumeric characters.") + end - expect(find('.username')).to have_css '.gl-field-error-outline' - end + it 'shows an error border if the username contains emojis' do + simulate_input('#new_user_username', 'ehsan😀') - it 'shows an error message if the username contains emojis' do - simulate_input('#new_user_username', 'ehsan😀') + expect(find('.username')).to have_css '.gl-field-error-outline' + end - expect(page).to have_content("Invalid input, please avoid emojis") - end + it 'shows an error message if the username contains emojis' do + simulate_input('#new_user_username', 'ehsan😀') - it 'shows a pending message if the username availability is being fetched', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/31484' do - fill_in 'new_user_username', with: 'new-user' + expect(page).to have_content("Invalid input, please avoid emojis") + end - expect(find('.username > .validation-pending')).not_to have_css '.hide' - end + it 'shows a pending message if the username availability is being fetched', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/31484' do + fill_in 'new_user_username', with: 'new-user' - it 'shows a success message if the username is available' do - fill_in 'new_user_username', with: 'new-user' - wait_for_requests + expect(find('.username > .validation-pending')).not_to have_css '.hide' + end - expect(find('.username > .validation-success')).not_to have_css '.hide' - end + it 'shows a success message if the username is available' do + fill_in 'new_user_username', with: 'new-user' + wait_for_requests - it 'shows an error message if the username is unavailable' do - existing_user = create(:user) + expect(find('.username > .validation-success')).not_to have_css '.hide' + end - fill_in 'new_user_username', with: existing_user.username - wait_for_requests + it 'shows an error message if the username is unavailable' do + existing_user = create(:user) - expect(find('.username > .validation-error')).not_to have_css '.hide' - end + fill_in 'new_user_username', with: existing_user.username + wait_for_requests - it 'shows a success message if the username is corrected and then available' do - fill_in 'new_user_username', with: 'new-user$' - wait_for_requests - fill_in 'new_user_username', with: 'new-user' - wait_for_requests + expect(find('.username > .validation-error')).not_to have_css '.hide' + end - expect(page).to have_content("Username is available.") + it 'shows a success message if the username is corrected and then available' do + fill_in 'new_user_username', with: 'new-user$' + wait_for_requests + fill_in 'new_user_username', with: 'new-user' + wait_for_requests + + expect(page).to have_content("Username is available.") + end end - end - context 'with no errors' do - context 'when sending confirmation email' do - before do - stub_application_setting(send_user_confirmation_email: true) + context 'with no errors' do + context 'when sending confirmation email' do + before do + stub_application_setting(send_user_confirmation_email: true) + end + + context 'when soft email confirmation is not enabled' do + before do + stub_feature_flags(soft_email_confirmation: false) + end + + it 'creates the user account and sends a confirmation email, and pre-fills email address after confirming' do + visit new_user_registration_path + + fill_in_signup_form + + expect { click_button 'Register' }.to change { User.count }.by(1) + expect(page).to have_current_path users_almost_there_path, ignore_query: true + expect(page).to have_content("Please check your email (#{new_user.email}) to confirm your account") + + confirm_email + + expect(find_field('Username or email').value).to eq(new_user.email) + end + end + + context 'when soft email confirmation is enabled' do + before do + stub_feature_flags(soft_email_confirmation: true) + end + + it 'creates the user account and sends a confirmation email' do + visit new_user_registration_path + + fill_in_signup_form + + expect { click_button 'Register' }.to change { User.count }.by(1) + expect(page).to have_current_path users_sign_up_welcome_path, ignore_query: true + end + end end - context 'when soft email confirmation is not enabled' do + context "when not sending confirmation email" do before do - stub_feature_flags(soft_email_confirmation: false) + stub_application_setting(send_user_confirmation_email: false) end - it 'creates the user account and sends a confirmation email, and pre-fills email address after confirming' do + it 'creates the user account and goes to dashboard' do visit new_user_registration_path fill_in_signup_form + click_button "Register" - expect { click_button 'Register' }.to change { User.count }.by(1) - expect(page).to have_current_path users_almost_there_path, ignore_query: true - expect(page).to have_content("Please check your email (#{new_user.email}) to confirm your account") - - confirm_email - - expect(find_field('Username or email').value).to eq(new_user.email) + expect(page).to have_current_path users_sign_up_welcome_path, ignore_query: true end end - context 'when soft email confirmation is enabled' do + context 'with required admin approval enabled' do before do - stub_feature_flags(soft_email_confirmation: true) + stub_application_setting(require_admin_approval_after_user_signup: true) end - it 'creates the user account and sends a confirmation email' do + it 'creates the user but does not sign them in' do visit new_user_registration_path fill_in_signup_form expect { click_button 'Register' }.to change { User.count }.by(1) - expect(page).to have_current_path users_sign_up_welcome_path, ignore_query: true + expect(page).to have_current_path new_user_session_path, ignore_query: true + expect(page).to have_content("You have signed up successfully. However, we could not sign you in because your account is awaiting approval from your GitLab administrator") end end end - context "when not sending confirmation email" do - before do - stub_application_setting(send_user_confirmation_email: false) + context 'with errors' do + it "displays the errors" do + create(:user, email: new_user.email) + visit new_user_registration_path + + fill_in_signup_form + click_button "Register" + + expect(page).to have_current_path user_registration_path, ignore_query: true + expect(page).to have_content("error prohibited this user from being saved") + expect(page).to have_content("Email has already been taken") end - it 'creates the user account and goes to dashboard' do + it 'does not redisplay the password' do + create(:user, email: new_user.email) visit new_user_registration_path fill_in_signup_form click_button "Register" - expect(page).to have_current_path users_sign_up_welcome_path, ignore_query: true + expect(page).to have_current_path user_registration_path, ignore_query: true + expect(page.body).not_to match(/#{new_user.password}/) end end - context 'with required admin approval enabled' do + context 'when terms are enforced' do before do - stub_application_setting(require_admin_approval_after_user_signup: true) + enforce_terms end - it 'creates the user but does not sign them in' do + it 'renders text that the user confirms terms by clicking register' do visit new_user_registration_path + expect(page).to have_content(/By clicking Register, I agree that I have read and accepted the Terms of Use and Privacy Policy/) + fill_in_signup_form + click_button 'Register' - expect { click_button 'Register' }.to change { User.count }.by(1) - expect(page).to have_current_path new_user_session_path, ignore_query: true - expect(page).to have_content("You have signed up successfully. However, we could not sign you in because your account is awaiting approval from your GitLab administrator") + expect(page).to have_current_path users_sign_up_welcome_path, ignore_query: true end end - end - context 'with errors' do - it "displays the errors" do - create(:user, email: new_user.email) + context 'when reCAPTCHA and invisible captcha are enabled' do + before do + stub_application_setting(invisible_captcha_enabled: true) + stub_application_setting(recaptcha_enabled: true) + allow_next_instance_of(RegistrationsController) do |instance| + allow(instance).to receive(:verify_recaptcha).and_return(true) + end + end + + context 'when reCAPTCHA detects malicious behaviour' do + before do + allow_next_instance_of(RegistrationsController) do |instance| + allow(instance).to receive(:verify_recaptcha).and_return(false) + end + end + + it 'prevents from signing up' do + visit new_user_registration_path + + fill_in_signup_form + + expect { click_button 'Register' }.not_to change { User.count } + expect(page).to have_content(_('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.')) + end + end + + context 'when invisible captcha detects malicious behaviour' do + it 'prevents from signing up' do + visit new_user_registration_path + + fill_in_signup_form + + expect { click_button 'Register' }.not_to change { User.count } + expect(page).to have_content('That was a bit too quick! Please resubmit.') + end + end + end + + it 'redirects to step 2 of the signup process, sets the role and redirects back' do + stub_feature_flags(about_your_company_registration_flow: false) visit new_user_registration_path fill_in_signup_form - click_button "Register" + click_button 'Register' - expect(page).to have_current_path user_registration_path, ignore_query: true - expect(page).to have_content("error prohibited this user from being saved") - expect(page).to have_content("Email has already been taken") + visit new_project_path + + expect(page).to have_current_path(users_sign_up_welcome_path) + + select 'Software Developer', from: 'user_role' + click_button 'Get started!' + + created_user = User.find_by_username(new_user.username) + + expect(created_user.software_developer_role?).to be_truthy + expect(created_user.setup_for_company).to be_nil + expect(page).to have_current_path(new_project_path) end it 'does not redisplay the password' do @@ -283,6 +378,12 @@ RSpec.describe 'Signup' do expect(page).to have_current_path user_registration_path, ignore_query: true expect(page.body).not_to match(/#{new_user.password}/) end + + context 'with invalid email', :saas, :js do + it_behaves_like 'user email validation' do + let(:path) { new_user_registration_path } + end + end end context 'when terms are enforced' do @@ -298,69 +399,21 @@ RSpec.describe 'Signup' do fill_in_signup_form click_button 'Register' - expect(page).to have_current_path users_sign_up_welcome_path, ignore_query: true - end - end - - context 'when reCAPTCHA and invisible captcha are enabled' do - before do - stub_application_setting(invisible_captcha_enabled: true) - stub_application_setting(recaptcha_enabled: true) - allow_next_instance_of(RegistrationsController) do |instance| - allow(instance).to receive(:verify_recaptcha).and_return(true) - end - end - - context 'when reCAPTCHA detects malicious behaviour' do - before do - allow_next_instance_of(RegistrationsController) do |instance| - allow(instance).to receive(:verify_recaptcha).and_return(false) - end - end - - it 'prevents from signing up' do - visit new_user_registration_path + visit new_project_path - fill_in_signup_form + expect(page).to have_current_path(users_sign_up_welcome_path) - expect { click_button 'Register' }.not_to change { User.count } - expect(page).to have_content(_('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.')) - end - end - - context 'when invisible captcha detects malicious behaviour' do - it 'prevents from signing up' do - visit new_user_registration_path + select 'Software Developer', from: 'user_role' + click_button 'Get started!' - fill_in_signup_form + created_user = User.find_by_username(new_user.username) - expect { click_button 'Register' }.not_to change { User.count } - expect(page).to have_content('That was a bit too quick! Please resubmit.') - end + expect(created_user.software_developer_role?).to be_truthy + expect(created_user.setup_for_company).to be_nil + expect(page).to have_current_path(new_project_path) end - end - - it 'redirects to step 2 of the signup process, sets the role and redirects back' do - stub_feature_flags(about_your_company_registration_flow: false) - visit new_user_registration_path - - fill_in_signup_form - click_button 'Register' - - visit new_project_path - expect(page).to have_current_path(users_sign_up_welcome_path) - - select 'Software Developer', from: 'user_role' - click_button 'Get started!' - - created_user = User.find_by_username(new_user.username) - - expect(created_user.software_developer_role?).to be_truthy - expect(created_user.setup_for_company).to be_nil - expect(page).to have_current_path(new_project_path) + it_behaves_like 'Signup name validation', 'new_user_first_name', 127, 'First name' + it_behaves_like 'Signup name validation', 'new_user_last_name', 127, 'Last name' end - - it_behaves_like 'Signup name validation', 'new_user_first_name', 127, 'First name' - it_behaves_like 'Signup name validation', 'new_user_last_name', 127, 'Last name' end |