# frozen_string_literal: true require 'spec_helper' RSpec.describe 'Admin::Users::User', feature_category: :user_management do include Spec::Support::Helpers::Features::AdminUsersHelpers include Spec::Support::Helpers::ModalHelpers let_it_be(:user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') } let_it_be(:current_user) { create(:admin) } before do sign_in(current_user) gitlab_enable_admin_mode_sign_in(current_user) end describe 'GET /admin/users/:id' do it 'has user info', :js, :aggregate_failures do visit admin_user_path(user) expect(page).to have_content(user.email) expect(page).to have_content(user.name) expect(page).to have_content("ID: #{user.id}") expect(page).to have_content("Namespace ID: #{user.namespace_id}") click_user_dropdown_toggle(user.id) expect(page).to have_button('Block') expect(page).to have_button('Deactivate') expect(page).to have_button('Delete user') expect(page).to have_button('Delete user and contributions') end context 'when blocking/unblocking the user' do it 'shows confirmation and allows blocking and unblocking', :js do visit admin_user_path(user) click_action_in_user_dropdown(user.id, 'Block') expect(page).to have_content('Block user') expect(page).to have_content('You can always unblock their account, their data will remain intact.') find('.modal-footer button', text: 'Block').click wait_for_requests expect(page).to have_content('Successfully blocked') click_action_in_user_dropdown(user.id, 'Unblock') expect(page).to have_content('Unblock user') expect(page).to have_content('You can always block their account again if needed.') find('.modal-footer button', text: 'Unblock').click expect(page).to have_content('Successfully unblocked') click_user_dropdown_toggle(user.id) expect(page).to have_content('Block') end end context 'when deactivating/re-activating the user' do it 'shows confirmation and allows deactivating/re-activating', :js do visit admin_user_path(user) click_action_in_user_dropdown(user.id, 'Deactivate') expect(page).to have_content('Deactivate user') expect(page).to have_content('You can always re-activate their account, their data will remain intact.') find('.modal-footer button', text: 'Deactivate').click wait_for_requests expect(page).to have_content('Successfully deactivated') click_action_in_user_dropdown(user.id, 'Activate') expect(page).to have_content('Activate user') expect(page).to have_content('You can always deactivate their account again if needed.') find('.modal-footer button', text: 'Activate').click wait_for_requests expect(page).to have_content('Successfully activated') click_user_dropdown_toggle(user.id) expect(page).to have_content('Deactivate') end end context 'when user is the sole owner of a group' do let_it_be(:group) { create(:group) } let_it_be(:user_sole_owner_of_group) { create(:user) } before do group.add_owner(user_sole_owner_of_group) end it 'shows `Delete user and contributions` action but not `Delete user` action', :js do visit admin_user_path(user_sole_owner_of_group) click_user_dropdown_toggle(user_sole_owner_of_group.id) expect(page).to have_button('Delete user and contributions') expect(page).not_to have_button('Delete user', exact: true) end it 'allows user to be deleted by using the `Delete user and contributions` action', :js do visit admin_user_path(user_sole_owner_of_group) click_action_in_user_dropdown(user_sole_owner_of_group.id, 'Delete user and contributions') within_modal do fill_in('username', with: user_sole_owner_of_group.name) click_button('Delete user and contributions') end wait_for_requests expect(page).to have_content('The user is being deleted.') end end context 'when a user is locked', time_travel_to: '2020-02-02 10:30:45 -0700' do let_it_be(:locked_user) { create(:user, locked_at: DateTime.parse('2020-02-02 10:30:00 -0700')) } before do visit admin_user_path(locked_user) end it "displays `(Locked)` next to user's name" do expect(page).to have_content("#{locked_user.name} (Locked)") end it 'allows a user to be unlocked from the `User administration dropdown', :js do accept_gl_confirm("Unlock user #{locked_user.name}?", button_text: 'Unlock') do click_action_in_user_dropdown(locked_user.id, 'Unlock') end expect(page).not_to have_content("#{locked_user.name} (Locked)") end end describe 'Impersonation' do let_it_be(:another_user) { create(:user) } context 'before impersonating' do subject { visit admin_user_path(user_to_visit) } let_it_be(:user_to_visit) { another_user } shared_examples "user that cannot be impersonated" do it 'disables impersonate button' do subject impersonate_btn = find('[data-testid="impersonate_user_link"]') expect(impersonate_btn).not_to be_nil expect(impersonate_btn['disabled']).not_to be_nil end it "shows tooltip with correct error message" do subject expect(find("span[title='#{impersonation_error_msg}']")).not_to be_nil end end context 'for other users' do it 'shows impersonate button for other users' do subject expect(page).to have_content('Impersonate') impersonate_btn = find('[data-testid="impersonate_user_link"]') expect(impersonate_btn['disabled']).to be_nil end end context 'for admin itself' do let(:user_to_visit) { current_user } it 'does not show impersonate button for admin itself' do subject expect(page).not_to have_content('Impersonate') end end context 'for blocked user' do let_it_be(:blocked_user) { create(:user, :blocked) } let(:user_to_visit) { blocked_user } let(:impersonation_error_msg) { _('You cannot impersonate a blocked user') } it_behaves_like "user that cannot be impersonated" end context 'for user with expired password' do let(:user_to_visit) do another_user.update!(password_expires_at: Time.zone.now - 5.minutes) another_user end let(:impersonation_error_msg) { _("You cannot impersonate a user with an expired password") } it_behaves_like "user that cannot be impersonated" end context 'for internal user' do let_it_be(:internal_user) { create(:user, :bot) } let(:user_to_visit) { internal_user } let(:impersonation_error_msg) { _("You cannot impersonate an internal user") } it_behaves_like "user that cannot be impersonated" end context 'for locked user' do let_it_be(:locked_user) { create(:user, :locked) } let(:user_to_visit) { locked_user } let(:impersonation_error_msg) { _("You cannot impersonate a user who cannot log in") } it_behaves_like "user that cannot be impersonated" end context 'when already impersonating another user' do let_it_be(:admin_user) { create(:user, :admin) } let(:impersonation_error_msg) { _("You are already impersonating another user") } subject do visit admin_user_path(admin_user) click_link 'Impersonate' visit admin_user_path(another_user) end it_behaves_like "user that cannot be impersonated" end context 'when impersonation is disabled' do before do stub_config_setting(impersonation_enabled: false) end it 'does not show impersonate button' do subject expect(page).not_to have_content('Impersonate') end end end context 'when impersonating' do subject { click_link 'Impersonate' } before do visit admin_user_path(another_user) end it 'logs in as the user when impersonate is clicked' do subject find('[data-testid="user-menu"]').click expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eql(another_user.username) end it 'sees impersonation log out icon' do subject icon = first('[data-testid="incognito-icon"]') expect(icon).not_to be nil end context 'when viewing the confirm email warning', :js do let_it_be(:another_user) { create(:user, :unconfirmed) } let(:warning_alert) { page.find(:css, '[data-testid="alert-warning"]') } let(:expected_styling) { { 'pointer-events' => 'none', 'cursor' => 'default' } } context 'with an email that does not contain HTML' do before do subject end it 'displays the warning alert including the email' do expect(warning_alert.text).to include("Please check your email (#{another_user.email}) to verify") end end context 'with an email that contains HTML' do let(:malicious_email) { "malicious@test.com