Commit 4da5e56d authored by Peter Hegman's avatar Peter Hegman

Merge branch...

Merge branch '342133-leading-trailing-whitespace-in-the-name-field-when-creating-a-user-are-persisted' into 'master'

Strip leading and trailing whitespace from user's name

See merge request gitlab-org/gitlab!76706
parents 0b3dc602 51ce5737
......@@ -57,14 +57,17 @@ export default {
};
},
computed: {
trimmedUsername() {
return this.username.trim();
},
modalTitle() {
return sprintf(this.title, { username: this.username }, false);
return sprintf(this.title, { username: this.trimmedUsername }, false);
},
secondaryButtonLabel() {
return s__('AdminUsers|Block user');
},
canSubmit() {
return this.enteredUsername === this.username;
return this.enteredUsername === this.trimmedUsername;
},
obstacles() {
try {
......@@ -104,7 +107,7 @@ export default {
<p>
<gl-sprintf :message="content">
<template #username>
<strong>{{ username }}</strong>
<strong>{{ trimmedUsername }}</strong>
</template>
<template #strong="props">
<strong>{{ props.content }}</strong>
......@@ -115,13 +118,13 @@ export default {
<user-deletion-obstacles-list
v-if="obstacles.length"
:obstacles="obstacles"
:user-name="username"
:user-name="trimmedUsername"
/>
<p>
<gl-sprintf :message="s__('AdminUsers|To confirm, type %{username}')">
<template #username>
<code class="gl-white-space-pre-wrap">{{ username }}</code>
<code class="gl-white-space-pre-wrap">{{ trimmedUsername }}</code>
</template>
</gl-sprintf>
</p>
......
......@@ -27,6 +27,7 @@ class User < ApplicationRecord
include HasUserType
include Gitlab::Auth::Otp::Fortinet
include RestrictedSignup
include StripAttribute
DEFAULT_NOTIFICATION_LEVEL = :participating
......@@ -466,6 +467,8 @@ class User < ApplicationRecord
scope :by_provider_and_extern_uid, ->(provider, extern_uid) { joins(:identities).merge(Identity.with_extern_uid(provider, extern_uid)) }
scope :get_ids_by_username, -> (username) { where(username: username).pluck(:id) }
strip_attributes! :name
def preferred_language
read_attribute('preferred_language') ||
I18n.default_locale.to_s.presence_in(Gitlab::I18n.available_locales) ||
......
......@@ -78,3 +78,83 @@ exports[`User Operation confirmation modal renders modal with form included 1`]
</gl-button-stub>
</div>
`;
exports[`User Operation confirmation modal when user's name has leading and trailing whitespace displays user's name without whitespace 1`] = `
<div>
<p>
content
</p>
<user-deletion-obstacles-list-stub
obstacles="schedule1,policy1"
username="John Smith"
/>
<p>
To confirm, type
<code
class="gl-white-space-pre-wrap"
>
John Smith
</code>
</p>
<form
action="delete-url"
method="post"
>
<input
name="_method"
type="hidden"
value="delete"
/>
<input
name="authenticity_token"
type="hidden"
value="csrf"
/>
<gl-form-input-stub
autocomplete="off"
autofocus=""
name="username"
type="text"
value=""
/>
</form>
<gl-button-stub
buttontextclasses=""
category="primary"
icon=""
size="medium"
variant="default"
>
Cancel
</gl-button-stub>
<gl-button-stub
buttontextclasses=""
category="secondary"
disabled="true"
icon=""
size="medium"
variant="danger"
>
secondaryAction
</gl-button-stub>
<gl-button-stub
buttontextclasses=""
category="primary"
disabled="true"
icon=""
size="medium"
variant="danger"
>
action
</gl-button-stub>
</div>
`;
import { GlButton, GlFormInput } from '@gitlab/ui';
import { GlButton, GlFormInput, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import DeleteUserModal from '~/admin/users/components/modals/delete_user_modal.vue';
import UserDeletionObstaclesList from '~/vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list.vue';
......@@ -35,7 +35,7 @@ describe('User Operation confirmation modal', () => {
const badUsername = 'bad_username';
const userDeletionObstacles = '["schedule1", "policy1"]';
const createComponent = (props = {}) => {
const createComponent = (props = {}, stubs = {}) => {
wrapper = shallowMount(DeleteUserModal, {
propsData: {
username,
......@@ -51,6 +51,7 @@ describe('User Operation confirmation modal', () => {
},
stubs: {
GlModal: ModalStub,
...stubs,
},
});
};
......@@ -150,6 +151,30 @@ describe('User Operation confirmation modal', () => {
});
});
describe("when user's name has leading and trailing whitespace", () => {
beforeEach(() => {
createComponent(
{
username: ' John Smith ',
},
{ GlSprintf },
);
});
it("displays user's name without whitespace", () => {
expect(wrapper.element).toMatchSnapshot();
});
it("shows enabled buttons when user's name is entered without whitespace", async () => {
setUsername('John Smith');
await wrapper.vm.$nextTick();
expect(findPrimaryButton().attributes('disabled')).toBeUndefined();
expect(findSecondaryButton().attributes('disabled')).toBeUndefined();
});
});
describe('Related user-deletion-obstacles list', () => {
it('does NOT render the list when user has no related obstacles', () => {
createComponent({ userDeletionObstacles: '[]' });
......
......@@ -1080,6 +1080,16 @@ RSpec.describe User do
end
end
context 'strip attributes' do
context 'name' do
let(:user) { User.new(name: ' John Smith ') }
it 'strips whitespaces on validation' do
expect { user.valid? }.to change { user.name }.to('John Smith')
end
end
end
describe 'Respond to' do
it { is_expected.to respond_to(:admin?) }
it { is_expected.to respond_to(:name) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment