generated from graasp/graasp-repo
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: allow to change username (#180)
fix: test allow to change username fix: Cypress test related to username validation
- Loading branch information
1 parent
64011c2
commit 16012f4
Showing
18 changed files
with
310 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,68 @@ | ||
describe('template spec', () => { | ||
it('passes', () => { | ||
cy.setUpApi(); | ||
import { | ||
USERNAME_CANCEL_BUTTON_ID, | ||
USERNAME_DISPLAY_ID, | ||
USERNAME_EDIT_BUTTON_ID, | ||
USERNAME_SAVE_BUTTON_ID, | ||
} from '@/config/selectors'; | ||
|
||
import { BOB, MEMBERS } from '../fixtures/members'; | ||
|
||
const changeUsername = (newUserName: string) => { | ||
cy.get(`#${USERNAME_EDIT_BUTTON_ID}`).click(); | ||
cy.get('input[name=username]').clear(); | ||
// Find the input field and type the new username | ||
cy.get('input[name=username]').type(newUserName); | ||
}; | ||
|
||
describe('Change username', () => { | ||
beforeEach(() => { | ||
cy.setUpApi({ currentMember: BOB }); | ||
cy.visit('/'); | ||
}); | ||
|
||
it('Username field connot be empty', () => { | ||
changeUsername('validUsername'); | ||
cy.get('input[name=username]').clear(); | ||
cy.get(`#${USERNAME_SAVE_BUTTON_ID}`).should('be.disabled'); | ||
}); | ||
|
||
it('Username too long', () => { | ||
const longUsername = MEMBERS.WRONG_NAME_TOO_LONG.name; | ||
changeUsername(longUsername); | ||
|
||
cy.get(`#${USERNAME_SAVE_BUTTON_ID}`).should('be.disabled'); | ||
}); | ||
|
||
it('Username too short', () => { | ||
const shortUsername = MEMBERS.WRONG_NAME_TOO_SHORT.name; | ||
changeUsername(shortUsername); | ||
cy.get(`#${USERNAME_SAVE_BUTTON_ID}`).should('be.disabled'); | ||
}); | ||
|
||
it('Valid username can be saved', () => { | ||
const validUsername = 'validUsername'; | ||
changeUsername(validUsername); | ||
cy.get(`#${USERNAME_SAVE_BUTTON_ID}`).should('not.be.disabled'); | ||
|
||
cy.get(`#${USERNAME_SAVE_BUTTON_ID}`).click(); | ||
|
||
cy.wait('@editMember').then(({ request: { body } }) => { | ||
expect(body.name).to.equal(validUsername); | ||
}); | ||
}); | ||
|
||
it('Should not update the user name if canceling edit', () => { | ||
changeUsername('validUsername'); | ||
cy.get(`#${USERNAME_CANCEL_BUTTON_ID}`).click(); | ||
cy.get(`#${USERNAME_DISPLAY_ID}`).contains(BOB.name); | ||
}); | ||
|
||
it('Saves username after trimming trailing space', () => { | ||
const usernameWithTrailingSpace = 'test '; // Nom d'utilisateur avec espace à la fin | ||
changeUsername(usernameWithTrailingSpace); | ||
cy.get(`#${USERNAME_SAVE_BUTTON_ID}`).click(); | ||
cy.wait('@editMember').then(({ request }) => { | ||
expect(request.body.name).to.equal(usernameWithTrailingSpace.trim()); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,28 @@ | ||
import { MemberFactory } from '@graasp/sdk'; | ||
import { Member, MemberFactory } from '@graasp/sdk'; | ||
|
||
// eslint-disable-next-line import/prefer-default-export | ||
export const CURRENT_MEMBER = MemberFactory(); | ||
|
||
export const MEMBERS = []; | ||
export const BOB = MemberFactory({ | ||
id: 'e1a0a49d-dfc4-466e-8379-f3846cda91e2', | ||
name: 'BOB', | ||
email: '[email protected]', | ||
}); | ||
export const MEMBERS: { | ||
[name: string]: Member & { | ||
nameValid?: boolean; | ||
}; | ||
} = { | ||
WRONG_NAME_TOO_SHORT: { | ||
id: '201621f0-848b-413f-80f9-25937a56c008', | ||
name: 'w', | ||
email: '[email protected]', | ||
nameValid: false, | ||
}, | ||
WRONG_NAME_TOO_LONG: { | ||
id: 'a7e428e9-86d3-434a-b611-d930cf8380ec', | ||
name: 'qwertyuiopasdfghjklzxcvbnmqwert', | ||
email: '[email protected]', | ||
nameValid: false, | ||
}, | ||
VALID_NAME: BOB, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"extends": "../tsconfig.json", | ||
"compilerOptions": { | ||
"target": "es5", | ||
"lib": ["es5", "dom", "ES2021.String"], | ||
"types": ["cypress", "node", "vite/client"], | ||
"strictNullChecks": false, | ||
"strict": true, | ||
"sourceMap": false | ||
}, | ||
"include": ["**/*.ts", "cypress.d.ts"], | ||
"exclude": ["coverage", ".nyc_output"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { ChangeEvent, useState } from 'react'; | ||
|
||
import CloseIcon from '@mui/icons-material/Close'; | ||
import DoneIcon from '@mui/icons-material/Done'; | ||
import { IconButton, Stack, TextField } from '@mui/material'; | ||
|
||
import { useAccountTranslation } from '@/config/i18n'; | ||
import { | ||
USERNAME_CANCEL_BUTTON_ID, | ||
USERNAME_INPUT_FIELD_ID, | ||
USERNAME_SAVE_BUTTON_ID, | ||
} from '@/config/selectors'; | ||
|
||
const MIN_USERNAME_LENGTH = 3; | ||
const MAX_USERNAME_LENGTH = 30; | ||
|
||
type EditingUserNameFieldProps = { | ||
name: string; | ||
onSave: (newValue: string) => void; | ||
onCancel: () => void; | ||
}; | ||
|
||
const verifyUsername = (username: string) => { | ||
const trimmedUsername = username.trim(); | ||
if (trimmedUsername === '') { | ||
return 'USERNAME_EMPTY_ERROR'; | ||
} | ||
|
||
if ( | ||
trimmedUsername.length < MIN_USERNAME_LENGTH || | ||
trimmedUsername.length > MAX_USERNAME_LENGTH | ||
) { | ||
return 'USERNAME_LENGTH_ERROR'; | ||
} | ||
return null; | ||
}; | ||
|
||
const EditingUserNameField = ({ | ||
name, | ||
onSave, | ||
onCancel, | ||
}: EditingUserNameFieldProps): JSX.Element => { | ||
const [newUserName, setNewUserName] = useState(name); | ||
const { t } = useAccountTranslation(); | ||
const [error, setError] = useState<string | null>(); | ||
|
||
const handleChange = ({ target }: ChangeEvent<HTMLInputElement>) => { | ||
const { value } = target; | ||
setNewUserName(value); | ||
const errorMessage = verifyUsername(value); | ||
setError( | ||
errorMessage | ||
? t(errorMessage, { | ||
min: MIN_USERNAME_LENGTH, | ||
max: MAX_USERNAME_LENGTH, | ||
}) | ||
: null, | ||
); | ||
}; | ||
const handleSave = () => { | ||
const errorMessage = verifyUsername(newUserName); | ||
|
||
if (!errorMessage) { | ||
onSave(newUserName.trim()); | ||
} | ||
}; | ||
|
||
return ( | ||
<Stack direction="row" alignItems="center" spacing={1}> | ||
<TextField | ||
id={USERNAME_INPUT_FIELD_ID} | ||
variant="standard" | ||
type="text" | ||
name="username" | ||
value={newUserName} | ||
error={Boolean(error)} | ||
helperText={error} | ||
onChange={handleChange} | ||
autoFocus | ||
/> | ||
<IconButton | ||
type="reset" | ||
onClick={onCancel} | ||
id={USERNAME_CANCEL_BUTTON_ID} | ||
> | ||
<CloseIcon /> | ||
</IconButton> | ||
<IconButton | ||
id={USERNAME_SAVE_BUTTON_ID} | ||
onClick={handleSave} | ||
disabled={Boolean(error)} | ||
> | ||
<DoneIcon /> | ||
</IconButton> | ||
</Stack> | ||
); | ||
}; | ||
|
||
export default EditingUserNameField; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { useState } from 'react'; | ||
|
||
import { ModeEdit } from '@mui/icons-material'; | ||
import { Box, IconButton, Stack } from '@mui/material'; | ||
|
||
import { | ||
USERNAME_DISPLAY_ID, | ||
USERNAME_EDIT_BUTTON_ID, | ||
} from '@/config/selectors'; | ||
|
||
import { mutations } from '../../config/queryClient'; | ||
import EditingUserNameField from './EditingNameField'; | ||
|
||
type UsernameProps = { | ||
member: { | ||
id: string; | ||
name: string; | ||
}; | ||
}; | ||
const UsernameForm = ({ member }: UsernameProps): JSX.Element => { | ||
const { mutate: editMember } = mutations.useEditMember(); | ||
const [isEditing, setIsEditing] = useState(false); | ||
|
||
const handleSave = (newUserName: string) => { | ||
editMember({ | ||
id: member.id, | ||
name: newUserName, | ||
}); | ||
|
||
setIsEditing(false); | ||
}; | ||
|
||
const cancelEditing = () => { | ||
setIsEditing(false); | ||
}; | ||
|
||
if (isEditing) { | ||
return ( | ||
<EditingUserNameField | ||
name={member.name} | ||
onSave={handleSave} | ||
onCancel={cancelEditing} | ||
/> | ||
); | ||
} | ||
return ( | ||
<Stack direction="row" alignItems="center" spacing={1}> | ||
<Box id={USERNAME_DISPLAY_ID}>{member.name}</Box> | ||
<IconButton | ||
onClick={() => setIsEditing(true)} | ||
id={USERNAME_EDIT_BUTTON_ID} | ||
> | ||
<ModeEdit /> | ||
</IconButton> | ||
</Stack> | ||
); | ||
}; | ||
export default UsernameForm; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.