Skip to content

Commit 78d2b2a

Browse files
chore: Migrate updateProfile to ts (#3607)
1 parent ecdafdb commit 78d2b2a

File tree

5 files changed

+162
-26
lines changed

5 files changed

+162
-26
lines changed

src/pages/AccountSettings/tabs/Admin/DetailsSection/DetailsSection.test.jsx

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ beforeEach(() => {
3333
server.resetHandlers()
3434
})
3535

36+
afterEach(() => {
37+
vi.clearAllMocks()
38+
})
39+
3640
afterAll(() => {
3741
server.close()
3842
})
@@ -43,7 +47,7 @@ describe('DetailsSection', () => {
4347
const mutate = vi.fn()
4448
const addNotification = vi.fn()
4549

46-
useAddNotification.mockReturnValue(addNotification)
50+
vi.mocked(useAddNotification).mockReturnValue(addNotification)
4751
server.use(
4852
graphql.mutation('UpdateProfile', (info) => {
4953
mutate(info.variables)
@@ -52,15 +56,23 @@ describe('DetailsSection', () => {
5256
data: {
5357
updateProfile: {
5458
me: {
55-
username: 'donald duck',
5659
email: info.variables.input.email
5760
? info.variables.input.email
5861
59-
name: info.variables.input.name
60-
? info.variables.input.name
61-
: 'donald duck',
62-
avatarUrl: 'http://127.0.0.1/avatar-url',
63-
onboardingCompleted: false,
62+
onboardingCompleted: true,
63+
privateAccess: null,
64+
businessEmail: null,
65+
user: {
66+
name: info.variables.input.name
67+
? info.variables.input.name
68+
: 'donald duck',
69+
username: 'donald duck',
70+
avatarUrl: 'http://127.0.0.1/avatar-url',
71+
avatar: 'http://127.0.0.1/avatar-url',
72+
student: false,
73+
studentCreatedAt: null,
74+
studentUpdatedAt: null,
75+
},
6476
},
6577
},
6678
},
@@ -218,10 +230,15 @@ describe('DetailsSection', () => {
218230
)
219231
})
220232
})
221-
222233
describe('when mutation is not successful', () => {
223234
it('adds an error notification', async () => {
224235
const { user, addNotification } = setup()
236+
server.use(
237+
graphql.mutation('UpdateProfile', () => {
238+
return HttpResponse.error()
239+
})
240+
)
241+
225242
render(<DetailsSection name="donald duck" email="[email protected]" />, {
226243
wrapper,
227244
})

src/pages/AccountSettings/tabs/Profile/NameEmailCard/NameEmailCard.test.jsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,18 @@ describe('NameEmailCard', () => {
6060
updateProfile: {
6161
me: {
6262
email: json.variables.input.email || '',
63-
user: { name: json.variables.input.name || '' },
63+
privateAccess: null,
64+
onboardingCompleted: true,
65+
businessEmail: null,
66+
user: {
67+
name: json.variables.input.name || '',
68+
username: 'test',
69+
avatarUrl: 'http://127.0.0.1/avatar-url',
70+
avatar: 'http://127.0.0.1/avatar-url',
71+
student: false,
72+
studentCreatedAt: null,
73+
studentUpdatedAt: null,
74+
},
6475
},
6576
},
6677
},

src/services/user/useUpdateProfile.test.tsx

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ const user = {
1616
name: 'terry',
1717
avatarUrl: 'http://127.0.0.1/avatar-url',
1818
onboardingCompleted: false,
19+
privateAccess: true,
20+
businessEmail: null,
21+
student: false,
22+
studentCreatedAt: null,
23+
studentUpdatedAt: null,
1924
}
2025

2126
const queryClient = new QueryClient({
@@ -50,9 +55,19 @@ describe('useUpdateProfile', () => {
5055
server.use(
5156
graphql.mutation('UpdateProfile', (info) => {
5257
const newUser = {
53-
...user,
54-
name: info.variables.input.name,
5558
email: info.variables.input.email,
59+
onboardingCompleted: user.onboardingCompleted,
60+
privateAccess: user.privateAccess,
61+
businessEmail: user.businessEmail,
62+
user: {
63+
name: info.variables.input.name,
64+
username: user.username,
65+
avatarUrl: user.avatarUrl,
66+
avatar: user.avatarUrl,
67+
student: user.student,
68+
studentCreatedAt: user.studentCreatedAt,
69+
studentUpdatedAt: user.studentUpdatedAt,
70+
},
5671
}
5772

5873
return HttpResponse.json({
@@ -88,11 +103,19 @@ describe('useUpdateProfile', () => {
88103

89104
await waitFor(() =>
90105
expect(result.current.data).toStrictEqual({
91-
avatarUrl: 'http://127.0.0.1/avatar-url',
92106
93-
name: 'new name',
107+
privateAccess: true,
94108
onboardingCompleted: false,
95-
username: 'TerrySmithDC',
109+
businessEmail: null,
110+
user: {
111+
name: 'new name',
112+
username: 'TerrySmithDC',
113+
avatarUrl: 'http://127.0.0.1/avatar-url',
114+
avatar: 'http://127.0.0.1/avatar-url',
115+
student: false,
116+
studentCreatedAt: null,
117+
studentUpdatedAt: null,
118+
},
96119
})
97120
)
98121
})
@@ -108,11 +131,19 @@ describe('useUpdateProfile', () => {
108131

109132
await waitFor(() =>
110133
expect(result.current.data).toStrictEqual({
111-
avatarUrl: 'http://127.0.0.1/avatar-url',
112134
113-
name: 'new name',
135+
privateAccess: true,
114136
onboardingCompleted: false,
115-
username: 'TerrySmithDC',
137+
businessEmail: null,
138+
user: {
139+
name: 'new name',
140+
username: 'TerrySmithDC',
141+
avatarUrl: 'http://127.0.0.1/avatar-url',
142+
avatar: 'http://127.0.0.1/avatar-url',
143+
student: false,
144+
studentCreatedAt: null,
145+
studentUpdatedAt: null,
146+
},
116147
})
117148
)
118149
})
@@ -153,11 +184,19 @@ describe('useUpdateProfile', () => {
153184

154185
await waitFor(() =>
155186
expect(result.current.data).toStrictEqual({
156-
avatarUrl: 'http://127.0.0.1/avatar-url',
157187
158-
name: 'new name',
188+
privateAccess: true,
159189
onboardingCompleted: false,
160-
username: 'TerrySmithDC',
190+
businessEmail: null,
191+
user: {
192+
name: 'new name',
193+
username: 'TerrySmithDC',
194+
avatarUrl: 'http://127.0.0.1/avatar-url',
195+
avatar: 'http://127.0.0.1/avatar-url',
196+
student: false,
197+
studentCreatedAt: null,
198+
studentUpdatedAt: null,
199+
},
161200
})
162201
)
163202
})

src/services/user/useUpdateProfile.ts

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,68 @@
11
import { useMutation, useQueryClient } from '@tanstack/react-query'
2+
import { z } from 'zod'
23

34
import config from 'config'
45

56
import Api from 'shared/api'
7+
import { NetworkErrorObject, rejectNetworkError } from 'shared/api/helpers'
8+
9+
import { GoalsSchema, TypeProjectsSchema } from './useUser'
10+
11+
const CurrentUserFragment = z.object({
12+
email: z.string().nullable(),
13+
privateAccess: z.boolean().nullable(),
14+
onboardingCompleted: z.boolean(),
15+
businessEmail: z.string().nullable(),
16+
user: z.object({
17+
name: z.string().nullable(),
18+
username: z.string(),
19+
avatarUrl: z.string(),
20+
avatar: z.string(),
21+
student: z.boolean(),
22+
studentCreatedAt: z.string().nullable(),
23+
studentUpdatedAt: z.string().nullable(),
24+
}),
25+
trackingMetadata: z
26+
.object({
27+
service: z.string(),
28+
ownerid: z.number(),
29+
serviceId: z.string(),
30+
plan: z.string().nullable(),
31+
staff: z.boolean().nullable(),
32+
hasYaml: z.boolean(),
33+
bot: z.string().nullable(),
34+
delinquent: z.boolean().nullable(),
35+
didTrial: z.boolean().nullable(),
36+
planProvider: z.string().nullable(),
37+
planUserCount: z.number().nullable(),
38+
createdAt: z.string().nullable(),
39+
updatedAt: z.string().nullable(),
40+
profile: z
41+
.object({
42+
createdAt: z.string(),
43+
otherGoal: z.string().nullable(),
44+
typeProjects: TypeProjectsSchema,
45+
goals: GoalsSchema,
46+
})
47+
.nullable(),
48+
})
49+
.nullish(),
50+
})
51+
52+
const UpdateProfileResponseSchema = z.object({
53+
updateProfile: z
54+
.object({
55+
me: CurrentUserFragment.nullish(),
56+
error: z
57+
.discriminatedUnion('__typename', [
58+
z.object({
59+
__typename: z.literal('ValidationError'),
60+
}),
61+
])
62+
.nullish(),
63+
})
64+
.nullish(),
65+
})
666

767
const currentUserFragment = `
868
fragment CurrentUserFragment on Me {
@@ -26,7 +86,6 @@ fragment CurrentUserFragment on Me {
2686
plan
2787
staff
2888
hasYaml
29-
service
3089
bot
3190
delinquent
3291
didTrial
@@ -72,10 +131,20 @@ export function useUpdateProfile({ provider }: { provider: string }) {
72131
email,
73132
},
74133
},
75-
}).then((res) => res?.data?.updateProfile?.me)
134+
}).then((res) => {
135+
const parsedData = UpdateProfileResponseSchema.safeParse(res.data)
136+
if (!parsedData.success) {
137+
return rejectNetworkError({
138+
status: 404,
139+
data: {},
140+
dev: 'useUpdateProfile - 404 failed to parse',
141+
} satisfies NetworkErrorObject)
142+
}
143+
return parsedData.data.updateProfile?.me
144+
})
76145
},
77-
onSuccess: (user) => {
78-
queryClient.setQueryData(['currentUser', provider], () => user)
146+
onSuccess: (currentUser) => {
147+
queryClient.setQueryData(['currentUser', provider], () => currentUser)
79148

80149
if (config.IS_SELF_HOSTED) {
81150
queryClient.invalidateQueries(['SelfHostedCurrentUser'])

src/services/user/useUser.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { z } from 'zod'
55
import Api from 'shared/api'
66
import { NetworkErrorObject } from 'shared/api/helpers'
77

8-
const TypeProjectsSchema = z.array(
8+
export const TypeProjectsSchema = z.array(
99
z.union([
1010
z.literal('PERSONAL'),
1111
z.literal('YOUR_ORG'),
@@ -14,7 +14,7 @@ const TypeProjectsSchema = z.array(
1414
])
1515
)
1616

17-
const GoalsSchema = z.array(
17+
export const GoalsSchema = z.array(
1818
z.union([
1919
z.literal('STARTING_WITH_TESTS'),
2020
z.literal('IMPROVE_COVERAGE'),

0 commit comments

Comments
 (0)