Skip to content

Commit 6dacf70

Browse files
johnstcnjsjoeio
andauthored
fix: disable AccountForm when user is not allowed edit users (#3649)
* RED: add unit tests for AccountForm username field * GREEN: disable username field and button on account form when user edits are not allowed Co-authored-by: Joe Previte <jjprevite@gmail.com>
1 parent b9dd566 commit 6dacf70

File tree

4 files changed

+93
-4
lines changed

4 files changed

+93
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { screen } from "@testing-library/react"
2+
import { MockUser2 } from "../../testHelpers/entities"
3+
import { render } from "../../testHelpers/renderHelpers"
4+
import { AccountForm, AccountFormValues } from "./SettingsAccountForm"
5+
6+
// NOTE: it does not matter what the role props of MockUser are set to,
7+
// only that editable is set to true or false. This is passed from
8+
// the call to /authorization done by authXService
9+
describe("AccountForm", () => {
10+
describe("when editable is set to true", () => {
11+
it("allows updating username", async () => {
12+
// Given
13+
const mockInitialValues: AccountFormValues = {
14+
username: MockUser2.username,
15+
}
16+
17+
// When
18+
render(
19+
<AccountForm
20+
editable
21+
email={MockUser2.email}
22+
initialValues={mockInitialValues}
23+
isLoading={false}
24+
onSubmit={() => {
25+
return
26+
}}
27+
/>,
28+
)
29+
30+
// Then
31+
const el = await screen.findByLabelText("Username")
32+
expect(el).toBeEnabled()
33+
const btn = await screen.findByRole("button", { name: /Update settings/i })
34+
expect(btn).toBeEnabled()
35+
})
36+
})
37+
38+
describe("when editable is set to false", () => {
39+
it("does not allow updating username", async () => {
40+
// Given
41+
const mockInitialValues: AccountFormValues = {
42+
username: MockUser2.username,
43+
}
44+
45+
// When
46+
render(
47+
<AccountForm
48+
editable={false}
49+
email={MockUser2.email}
50+
initialValues={mockInitialValues}
51+
isLoading={false}
52+
onSubmit={() => {
53+
return
54+
}}
55+
/>,
56+
)
57+
58+
// Then
59+
const el = await screen.findByLabelText("Username")
60+
expect(el).toBeDisabled()
61+
const btn = await screen.findByRole("button", { name: /Update settings/i })
62+
expect(btn).toBeDisabled()
63+
})
64+
})
65+
})

site/src/components/SettingsAccountForm/SettingsAccountForm.tsx

+12-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { getFormHelpersWithError, nameValidator, onChangeTrimmed } from "../../u
77
import { LoadingButton } from "../LoadingButton/LoadingButton"
88
import { Stack } from "../Stack/Stack"
99

10-
interface AccountFormValues {
10+
export interface AccountFormValues {
1111
username: string
1212
}
1313

@@ -22,6 +22,7 @@ const validationSchema = Yup.object({
2222
})
2323

2424
export interface AccountFormProps {
25+
editable: boolean
2526
email: string
2627
isLoading: boolean
2728
initialValues: AccountFormValues
@@ -32,6 +33,7 @@ export interface AccountFormProps {
3233
}
3334

3435
export const AccountForm: FC<React.PropsWithChildren<AccountFormProps>> = ({
36+
editable,
3537
email,
3638
isLoading,
3739
onSubmit,
@@ -62,14 +64,22 @@ export const AccountForm: FC<React.PropsWithChildren<AccountFormProps>> = ({
6264
<TextField
6365
{...getFieldHelpers("username")}
6466
onChange={onChangeTrimmed(form)}
67+
aria-disabled={!editable}
6568
autoComplete="username"
69+
disabled={!editable}
6670
fullWidth
6771
label={Language.usernameLabel}
6872
variant="outlined"
6973
/>
7074

7175
<div>
72-
<LoadingButton loading={isLoading} type="submit" variant="contained">
76+
<LoadingButton
77+
loading={isLoading}
78+
aria-disabled={!editable}
79+
disabled={!editable}
80+
type="submit"
81+
variant="contained"
82+
>
7383
{isLoading ? "" : Language.updateSettings}
7484
</LoadingButton>
7585
</div>

site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export const Language = {
1111
export const AccountPage: React.FC = () => {
1212
const xServices = useContext(XServiceContext)
1313
const [authState, authSend] = useActor(xServices.authXService)
14-
const { me, updateProfileError } = authState.context
14+
const { me, permissions, updateProfileError } = authState.context
15+
const canEditUsers = permissions && permissions.updateUsers
1516

1617
if (!me) {
1718
throw new Error("No current user found")
@@ -20,10 +21,13 @@ export const AccountPage: React.FC = () => {
2021
return (
2122
<Section title={Language.title}>
2223
<AccountForm
24+
editable={Boolean(canEditUsers)}
2325
email={me.email}
2426
updateProfileError={updateProfileError}
2527
isLoading={authState.matches("signedIn.profile.updatingProfile")}
26-
initialValues={{ username: me.username }}
28+
initialValues={{
29+
username: me.username,
30+
}}
2731
onSubmit={(data) => {
2832
authSend({
2933
type: "UPDATE_PROFILE",

site/src/testHelpers/entities.ts

+10
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ export const MockUser: TypesGen.User = {
6161
roles: [MockOwnerRole],
6262
}
6363

64+
export const MockUserAdmin: TypesGen.User = {
65+
id: "test-user",
66+
username: "TestUser",
67+
email: "test@coder.com",
68+
created_at: "",
69+
status: "active",
70+
organization_ids: ["fc0774ce-cc9e-48d4-80ae-88f7a4d4a8b0"],
71+
roles: [MockUserAdminRole],
72+
}
73+
6474
export const MockUser2: TypesGen.User = {
6575
id: "test-user-2",
6676
username: "TestUser2",

0 commit comments

Comments
 (0)