diff --git a/frontend/src/component/admin/roles/RoleForm/RoleForm.tsx b/frontend/src/component/admin/roles/RoleForm/RoleForm.tsx index e04e19c9802f..6732f1d79846 100644 --- a/frontend/src/component/admin/roles/RoleForm/RoleForm.tsx +++ b/frontend/src/component/admin/roles/RoleForm/RoleForm.tsx @@ -28,10 +28,10 @@ const StyledInputFullWidth = styled(Input)({ interface IRoleFormProps { type?: PredefinedRoleType; name: string; - setName: React.Dispatch>; + setName: (name: string) => void; validateName: (name: string) => boolean; description: string; - setDescription: React.Dispatch>; + setDescription: (description: string) => void; validateDescription: (description: string) => boolean; checkedPermissions: ICheckedPermissions; setCheckedPermissions: React.Dispatch< diff --git a/frontend/src/component/admin/roles/RoleForm/useRoleForm.test.ts b/frontend/src/component/admin/roles/RoleForm/useRoleForm.test.ts new file mode 100644 index 000000000000..5ce1b216a0b2 --- /dev/null +++ b/frontend/src/component/admin/roles/RoleForm/useRoleForm.test.ts @@ -0,0 +1,50 @@ +import { renderHook } from '@testing-library/react'; +import { useRoleForm } from './useRoleForm'; +import { act } from 'react-test-renderer'; +import { test } from 'vitest'; + +describe('trim names and description', () => { + test('name is trimmed before being set', () => { + const { result } = renderHook(() => useRoleForm()); + + act(() => { + result.current.setName(' my role '); + }); + + expect(result.current.name).toBe('my role'); + }); + + test('description is trimmed before being set', () => { + const { result } = renderHook(() => useRoleForm()); + + act(() => { + result.current.setDescription(' my description '); + }); + + expect(result.current.description).toBe('my description'); + }); + + test('name that is just whitespace triggers an error', () => { + const { result } = renderHook(() => useRoleForm()); + + act(() => { + result.current.validateName(' '); + }); + + expect(result.current.errors).toMatchObject({ + name: 'Name is required.', + }); + }); + + test('description that is just whitespace triggers an error', () => { + const { result } = renderHook(() => useRoleForm()); + + act(() => { + result.current.validateDescription(' '); + }); + + expect(result.current.errors).toMatchObject({ + description: 'Description is required.', + }); + }); +}); diff --git a/frontend/src/component/admin/roles/RoleForm/useRoleForm.ts b/frontend/src/component/admin/roles/RoleForm/useRoleForm.ts index a02334f9b86f..f16f3e11dce9 100644 --- a/frontend/src/component/admin/roles/RoleForm/useRoleForm.ts +++ b/frontend/src/component/admin/roles/RoleForm/useRoleForm.ts @@ -27,7 +27,10 @@ export const useRoleForm = ( const { roles, projectRoles } = useRoles(); const [name, setName] = useState(initialName); + const setTrimmedName = (newName: string) => setName(newName.trim()); const [description, setDescription] = useState(initialDescription); + const setTrimmedDescription = (newDescription: string) => + setDescription(newDescription.trim()); const [checkedPermissions, setCheckedPermissions] = useState({}); const [errors, setErrors] = useState(DEFAULT_ERRORS); @@ -78,12 +81,13 @@ export const useRoleForm = ( }; const validateName = (name: string) => { - if (!isNotEmpty(name)) { + const trimmedName = name.trim(); + if (!isNotEmpty(trimmedName)) { setError(ErrorField.NAME, 'Name is required.'); return false; } - if (!isNameUnique(name)) { + if (!isNameUnique(trimmedName)) { setError(ErrorField.NAME, 'Name must be unique.'); return false; } @@ -93,7 +97,8 @@ export const useRoleForm = ( }; const validateDescription = (description: string) => { - if (!isNotEmpty(description)) { + const trimmedDescription = description.trim(); + if (!isNotEmpty(trimmedDescription)) { setError(ErrorField.DESCRIPTION, 'Description is required.'); return false; } @@ -139,10 +144,10 @@ export const useRoleForm = ( return { name, - setName, + setName: setTrimmedName, validateName, description, - setDescription, + setDescription: setTrimmedDescription, validateDescription, checkedPermissions, setCheckedPermissions,