Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit14cdd85

Browse files
authored
fix(site): username validation in forms (#1851)
* refactor(site): move name validation to utils* fix(site): username validation in forms
1 parent8a5277e commit14cdd85

File tree

6 files changed

+72
-59
lines changed

6 files changed

+72
-59
lines changed

‎site/src/components/CreateUserForm/CreateUserForm.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { FormikContextType, FormikErrors, useFormik } from "formik"
44
importReactfrom"react"
55
import*asYupfrom"yup"
66
import*asTypesGenfrom"../../api/typesGenerated"
7-
import{getFormHelpers,onChangeTrimmed}from"../../util/formUtils"
7+
import{getFormHelpers,nameValidator,onChangeTrimmed}from"../../util/formUtils"
88
import{FormFooter}from"../FormFooter/FormFooter"
99
import{FullPageForm}from"../FullPageForm/FullPageForm"
1010

@@ -15,7 +15,6 @@ export const Language = {
1515
emailInvalid:"Please enter a valid email address.",
1616
emailRequired:"Please enter an email address.",
1717
passwordRequired:"Please enter a password.",
18-
usernameRequired:"Please enter a username.",
1918
createUser:"Create",
2019
cancel:"Cancel",
2120
}
@@ -32,7 +31,7 @@ export interface CreateUserFormProps {
3231
constvalidationSchema=Yup.object({
3332
email:Yup.string().trim().email(Language.emailInvalid).required(Language.emailRequired),
3433
password:Yup.string().required(Language.passwordRequired),
35-
username:Yup.string().required(Language.usernameRequired),
34+
username:nameValidator(Language.usernameLabel),
3635
})
3736

3837
exportconstCreateUserForm:React.FC<CreateUserFormProps>=({

‎site/src/components/SettingsAccountForm/SettingsAccountForm.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import TextField from "@material-ui/core/TextField"
33
import{FormikContextType,FormikErrors,useFormik}from"formik"
44
importReactfrom"react"
55
import*asYupfrom"yup"
6-
import{getFormHelpers,onChangeTrimmed}from"../../util/formUtils"
6+
import{getFormHelpers,nameValidator,onChangeTrimmed}from"../../util/formUtils"
77
import{LoadingButton}from"../LoadingButton/LoadingButton"
88
import{Stack}from"../Stack/Stack"
99

@@ -22,7 +22,7 @@ export const Language = {
2222

2323
constvalidationSchema=Yup.object({
2424
email:Yup.string().trim().email(Language.emailInvalid).required(Language.emailRequired),
25-
username:Yup.string().trim(),
25+
username:nameValidator(Language.usernameLabel),
2626
})
2727

2828
exporttypeAccountFormErrors=FormikErrors<AccountFormValues>
Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import{screen,waitFor}from"@testing-library/react"
22
importuserEventfrom"@testing-library/user-event"
33
importReactfrom"react"
4-
import{reach,StringSchema}from"yup"
54
import*asAPIfrom"../../api/api"
65
import{LanguageasFooterLanguage}from"../../components/FormFooter/FormFooter"
76
import{MockTemplate,MockWorkspace}from"../../testHelpers/entities"
87
import{renderWithAuth}from"../../testHelpers/renderHelpers"
8+
import{LanguageasFormLanguage}from"../../util/formUtils"
99
importCreateWorkspacePagefrom"./CreateWorkspacePage"
10-
import{Language,validationSchema}from"./CreateWorkspacePageView"
10+
import{Language}from"./CreateWorkspacePageView"
1111

1212
constrenderCreateWorkspacePage=()=>{
1313
returnrenderWithAuth(<CreateWorkspacePage/>,{
@@ -23,8 +23,6 @@ const fillForm = async ({ name = "example" }: { name?: string }) => {
2323
awaituserEvent.click(submitButton)
2424
}
2525

26-
constnameSchema=reach(validationSchema,"name")asStringSchema
27-
2826
describe("CreateWorkspacePage",()=>{
2927
it("renders",async()=>{
3028
renderCreateWorkspacePage()
@@ -35,7 +33,7 @@ describe("CreateWorkspacePage", () => {
3533
it("shows validation error message",async()=>{
3634
renderCreateWorkspacePage()
3735
awaitfillForm({name:"$$$"})
38-
consterrorMessage=awaitscreen.findByText(Language.nameMatches)
36+
consterrorMessage=awaitscreen.findByText(FormLanguage.nameInvalidChars(Language.nameLabel))
3937
expect(errorMessage).toBeDefined()
4038
})
4139

@@ -47,38 +45,4 @@ describe("CreateWorkspacePage", () => {
4745
// Check if the request was made
4846
awaitwaitFor(()=>expect(API.createWorkspace).toBeCalledTimes(1))
4947
})
50-
51-
describe("validationSchema",()=>{
52-
it("allows a 1-letter name",()=>{
53-
constvalidate=()=>nameSchema.validateSync("t")
54-
expect(validate).not.toThrow()
55-
})
56-
57-
it("allows a 32-letter name",()=>{
58-
constinput=Array(32).fill("a").join("")
59-
constvalidate=()=>nameSchema.validateSync(input)
60-
expect(validate).not.toThrow()
61-
})
62-
63-
it("allows 'test-3' to be used as name",()=>{
64-
constvalidate=()=>nameSchema.validateSync("test-3")
65-
expect(validate).not.toThrow()
66-
})
67-
68-
it("allows '3-test' to be used as a name",()=>{
69-
constvalidate=()=>nameSchema.validateSync("3-test")
70-
expect(validate).not.toThrow()
71-
})
72-
73-
it("disallows a 33-letter name",()=>{
74-
constinput=Array(33).fill("a").join("")
75-
constvalidate=()=>nameSchema.validateSync(input)
76-
expect(validate).toThrow()
77-
})
78-
79-
it("disallows a space",()=>{
80-
constvalidate=()=>nameSchema.validateSync("test 3")
81-
expect(validate).toThrow()
82-
})
83-
})
8448
})

‎site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,13 @@ import { Loader } from "../../components/Loader/Loader"
1010
import{Margins}from"../../components/Margins/Margins"
1111
import{ParameterInput}from"../../components/ParameterInput/ParameterInput"
1212
import{Stack}from"../../components/Stack/Stack"
13-
import{getFormHelpers,onChangeTrimmed}from"../../util/formUtils"
13+
import{getFormHelpers,nameValidator,onChangeTrimmed}from"../../util/formUtils"
1414

1515
exportconstLanguage={
1616
templateLabel:"Template",
1717
nameLabel:"Name",
18-
nameRequired:"Please enter a name.",
19-
nameMatches:"Name must start with a-Z or 0-9 and can contain a-Z, 0-9 or -",
20-
nameMax:"Name cannot be longer than 32 characters",
2118
}
2219

23-
// REMARK: Keep in sync with coderd/httpapi/httpapi.go#L40
24-
constmaxLenName=32
25-
26-
// REMARK: Keep in sync with coderd/httpapi/httpapi.go#L18
27-
constusernameRE=/^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*$/
28-
2920
exportinterfaceCreateWorkspacePageViewProps{
3021
loadingTemplates:boolean
3122
loadingTemplateSchema:boolean
@@ -39,10 +30,7 @@ export interface CreateWorkspacePageViewProps {
3930
}
4031

4132
exportconstvalidationSchema=Yup.object({
42-
name:Yup.string()
43-
.required(Language.nameRequired)
44-
.matches(usernameRE,Language.nameMatches)
45-
.max(maxLenName,Language.nameMax),
33+
name:nameValidator(Language.nameLabel),
4634
})
4735

4836
exportconstCreateWorkspacePageView:React.FC<CreateWorkspacePageViewProps>=(props)=>{

‎site/src/util/formUtils.test.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import{FormikContextType}from"formik/dist/types"
2-
import{getFormHelpers,onChangeTrimmed}from"./formUtils"
2+
import{getFormHelpers,nameValidator,onChangeTrimmed}from"./formUtils"
33

44
interfaceTestType{
55
untouchedGoodField:string
@@ -35,6 +35,8 @@ const form = {
3535
},
3636
}asunknownasFormikContextType<TestType>
3737

38+
constnameSchema=nameValidator("name")
39+
3840
describe("form util functions",()=>{
3941
describe("getFormHelpers",()=>{
4042
describe("without API errors",()=>{
@@ -94,4 +96,38 @@ describe("form util functions", () => {
9496
expect(mockHandleChange).toHaveBeenCalledWith({target:{value:"hello"}})
9597
})
9698
})
99+
100+
describe("nameValidator",()=>{
101+
it("allows a 1-letter name",()=>{
102+
constvalidate=()=>nameSchema.validateSync("a")
103+
expect(validate).not.toThrow()
104+
})
105+
106+
it("allows a 32-letter name",()=>{
107+
constinput=Array(32).fill("a").join("")
108+
constvalidate=()=>nameSchema.validateSync(input)
109+
expect(validate).not.toThrow()
110+
})
111+
112+
it("allows 'test-3' to be used as name",()=>{
113+
constvalidate=()=>nameSchema.validateSync("test-3")
114+
expect(validate).not.toThrow()
115+
})
116+
117+
it("allows '3-test' to be used as a name",()=>{
118+
constvalidate=()=>nameSchema.validateSync("3-test")
119+
expect(validate).not.toThrow()
120+
})
121+
122+
it("disallows a 33-letter name",()=>{
123+
constinput=Array(33).fill("a").join("")
124+
constvalidate=()=>nameSchema.validateSync(input)
125+
expect(validate).toThrow()
126+
})
127+
128+
it("disallows a space",()=>{
129+
constvalidate=()=>nameSchema.validateSync("test 3")
130+
expect(validate).toThrow()
131+
})
132+
})
97133
})

‎site/src/util/formUtils.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
import{FormikContextType,FormikErrors,getIn}from"formik"
22
import{ChangeEvent,ChangeEventHandler,FocusEventHandler,ReactNode}from"react"
3+
import*asYupfrom"yup"
4+
5+
exportconstLanguage={
6+
nameRequired:(name:string):string=>{
7+
return`Please enter a${name.toLowerCase()}.`
8+
},
9+
nameInvalidChars:(name:string):string=>{
10+
return`${name} must start with a-Z or 0-9 and can contain a-Z, 0-9 or -`
11+
},
12+
nameTooLong:(name:string):string=>{
13+
return`${name} cannot be longer than 32 characters`
14+
},
15+
}
316

417
interfaceFormHelpers{
518
name:string
@@ -38,3 +51,16 @@ export const onChangeTrimmed =
3851
event.target.value=event.target.value.trim()
3952
form.handleChange(event)
4053
}
54+
55+
// REMARK: Keep in sync with coderd/httpapi/httpapi.go#L40
56+
constmaxLenName=32
57+
58+
// REMARK: Keep in sync with coderd/httpapi/httpapi.go#L18
59+
constusernameRE=/^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*$/
60+
61+
// REMARK: see #1756 for name/username semantics
62+
exportconstnameValidator=(name:string):Yup.StringSchema=>
63+
Yup.string()
64+
.required(Language.nameRequired(name))
65+
.matches(usernameRE,Language.nameInvalidChars(name))
66+
.max(maxLenName,Language.nameTooLong(name))

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp