Skip to content

Commit 95c655d

Browse files
authored
ref: Migrate useGenerateUserToken to TSQ V5 (#3735)
1 parent d65e120 commit 95c655d

File tree

5 files changed

+120
-76
lines changed

5 files changed

+120
-76
lines changed

src/pages/AccountSettings/tabs/Access/CreateTokenModal.jsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,25 @@ import PropTypes from 'prop-types'
22
import { useState } from 'react'
33
import { useForm } from 'react-hook-form'
44

5-
import { useGenerateUserToken } from 'services/access'
5+
import { useGenerateUserToken } from 'services/access/useGenerateUserToken'
66
import Button from 'ui/Button'
77
import { CopyClipboard } from 'ui/CopyClipboard'
88
import Modal from 'ui/Modal'
99
import TextInput from 'ui/TextInput/TextInput'
1010

1111
function CreateTokenModal({ closeModal, provider }) {
12+
const [token, setToken] = useState(null)
1213
const { register, handleSubmit, watch } = useForm({
13-
defaultValues: {
14-
name: '',
15-
},
14+
defaultValues: { name: '' },
1615
})
1716
const nameValue = watch('name', '')
18-
19-
const [token, setToken] = useState(null)
20-
2117
const { mutate, isLoading } = useGenerateUserToken({ provider })
2218

2319
const submit = ({ name }) => {
2420
mutate(
2521
{ name },
2622
{
27-
onSuccess: ({ data }) => {
23+
onSuccess: (data) => {
2824
setToken(data?.createUserToken?.fullToken)
2925
},
3026
}

src/pages/AccountSettings/tabs/Access/CreateTokenModal.test.jsx

Lines changed: 70 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,55 @@
1-
import { render, screen, waitFor } from 'custom-testing-library'
2-
1+
import {
2+
QueryClientProvider as QueryClientProviderV5,
3+
QueryClient as QueryClientV5,
4+
} from '@tanstack/react-queryV5'
5+
import { render, screen, waitFor } from '@testing-library/react'
36
import userEvent from '@testing-library/user-event'
4-
5-
import { useGenerateUserToken } from 'services/access'
7+
import { graphql, HttpResponse } from 'msw'
8+
import { setupServer } from 'msw/node'
69

710
import CreateTokenModal from './CreateTokenModal'
811

9-
vi.mock('services/access')
12+
const queryClientV5 = new QueryClientV5({
13+
defaultOptions: { queries: { retry: false } },
14+
})
15+
16+
const wrapper = ({ children }) => (
17+
<QueryClientProviderV5 client={queryClientV5}>
18+
{children}
19+
</QueryClientProviderV5>
20+
)
21+
22+
const server = setupServer()
23+
24+
beforeAll(() => {
25+
server.listen()
26+
})
27+
28+
beforeEach(() => {
29+
queryClientV5.clear()
30+
server.resetHandlers()
31+
})
32+
33+
afterAll(() => {
34+
server.close()
35+
})
1036

1137
describe('CreateTokenModal', () => {
1238
function setup() {
1339
const user = userEvent.setup()
1440
const closeModal = vi.fn()
15-
const success = {
16-
data: {
17-
createUserToken: {
18-
fullToken: '111-222-333',
19-
},
20-
},
21-
}
22-
const mutate = vi.fn((_, { onSuccess }) => {
23-
return onSuccess(success)
24-
})
25-
useGenerateUserToken.mockReturnValue({
26-
mutate,
27-
})
41+
const mutateMock = vi.fn()
42+
43+
server.use(
44+
graphql.mutation('CreateUserToken', (info) => {
45+
mutateMock(info.variables)
46+
return HttpResponse.json({
47+
data: { createUserToken: { fullToken: '111-222-333', error: null } },
48+
})
49+
})
50+
)
2851

29-
return { mutate, closeModal, user }
52+
return { mutateMock, closeModal, user }
3053
}
3154

3255
describe('renders initial CreateTokenModal', () => {
@@ -38,7 +61,8 @@ describe('CreateTokenModal', () => {
3861
provider="gh"
3962
showModal={true}
4063
closeModal={closeModal}
41-
/>
64+
/>,
65+
{ wrapper }
4266
)
4367

4468
const title = screen.getByText(/Generate new API access token/)
@@ -52,7 +76,8 @@ describe('CreateTokenModal', () => {
5276
provider="gh"
5377
showModal={true}
5478
closeModal={closeModal}
55-
/>
79+
/>,
80+
{ wrapper }
5681
)
5782

5883
const label = screen.getByText(/Token Name/)
@@ -68,7 +93,8 @@ describe('CreateTokenModal', () => {
6893
provider="gh"
6994
showModal={true}
7095
closeModal={closeModal}
71-
/>
96+
/>,
97+
{ wrapper }
7298
)
7399

74100
const buttons = screen.getAllByRole('button')
@@ -78,21 +104,25 @@ describe('CreateTokenModal', () => {
78104

79105
describe('when the user types a token name and submits', () => {
80106
it('calls the mutation', async () => {
81-
const { mutate, closeModal, user } = setup()
107+
const { mutateMock, closeModal, user } = setup()
82108
render(
83109
<CreateTokenModal
84110
provider="gh"
85111
showModal={true}
86112
closeModal={closeModal}
87-
/>
113+
/>,
114+
{ wrapper }
88115
)
89116

90117
const input = screen.getByRole('textbox')
91118
await user.type(input, '2333')
92119
const generateToken = screen.getByText('Generate Token')
93120
await user.click(generateToken)
94121

95-
expect(mutate).toHaveBeenCalled()
122+
await waitFor(() => expect(mutateMock).toHaveBeenCalled())
123+
expect(mutateMock).toHaveBeenCalledWith({
124+
input: { name: '2333', tokenType: 'api' },
125+
})
96126
})
97127

98128
describe('when mutation is successful', () => {
@@ -103,7 +133,8 @@ describe('CreateTokenModal', () => {
103133
provider="gh"
104134
showModal={true}
105135
closeModal={closeModal}
106-
/>
136+
/>,
137+
{ wrapper }
107138
)
108139

109140
const title = await screen.findByText(/API access token/)
@@ -117,7 +148,8 @@ describe('CreateTokenModal', () => {
117148
provider="gh"
118149
showModal={true}
119150
closeModal={closeModal}
120-
/>
151+
/>,
152+
{ wrapper }
121153
)
122154

123155
const input = screen.getByRole('textbox')
@@ -136,41 +168,48 @@ describe('CreateTokenModal', () => {
136168
const warning = screen.getByText(/Make sure to copy your token now/)
137169
expect(warning).toBeInTheDocument()
138170
})
171+
139172
it('renders footer', async () => {
140173
const { closeModal, user } = setup()
141174
render(
142175
<CreateTokenModal
143176
provider="gh"
144177
showModal={true}
145178
closeModal={closeModal}
146-
/>
179+
/>,
180+
{ wrapper }
147181
)
148182

149183
const input = screen.getByRole('textbox')
150184
await user.type(input, '2333')
185+
151186
const generateToken = screen.getByText('Generate Token')
152187
await user.click(generateToken)
153188

154-
const button = screen.getByRole('button', {
189+
const button = await screen.findByRole('button', {
155190
name: /done/i,
156191
})
157192
expect(button).toBeInTheDocument()
158193
})
194+
159195
it('close modals', async () => {
160196
const { closeModal, user } = setup()
161197
render(
162198
<CreateTokenModal
163199
provider="gh"
164200
showModal={true}
165201
closeModal={closeModal}
166-
/>
202+
/>,
203+
{ wrapper }
167204
)
168205

169206
const input = screen.getByRole('textbox')
170207
await user.type(input, '2333')
208+
171209
const generateToken = screen.getByText('Generate Token')
172210
await user.click(generateToken)
173-
const done = screen.getByText('Done')
211+
212+
const done = await screen.findByText('Done')
174213
await user.click(done)
175214

176215
await waitFor(() => {

src/services/access/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/services/access/useGenerateUserToken.test.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
1+
import {
2+
QueryClientProvider as QueryClientProviderV5,
3+
QueryClient as QueryClientV5,
4+
} from '@tanstack/react-queryV5'
25
import { renderHook, waitFor } from '@testing-library/react'
36
import { graphql, HttpResponse } from 'msw'
47
import { setupServer } from 'msw/node'
58
import { PropsWithChildren } from 'react'
69
import { MemoryRouter, Route } from 'react-router-dom'
710

8-
import { useGenerateUserToken } from './index'
11+
import { useGenerateUserToken } from './useGenerateUserToken'
912

10-
const queryClient = new QueryClient({
13+
const queryClientV5 = new QueryClientV5({
1114
defaultOptions: { queries: { retry: false } },
1215
})
1316
const wrapper: React.FC<PropsWithChildren> = ({ children }) => (
14-
<MemoryRouter initialEntries={['/gh']}>
15-
<Route path="/:provider">
16-
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
17-
</Route>
18-
</MemoryRouter>
17+
<QueryClientProviderV5 client={queryClientV5}>
18+
<MemoryRouter initialEntries={['/gh']}>
19+
<Route path="/:provider">{children}</Route>
20+
</MemoryRouter>
21+
</QueryClientProviderV5>
1922
)
2023

2124
const provider = 'gh'
@@ -27,7 +30,7 @@ beforeAll(() => {
2730

2831
beforeEach(() => {
2932
server.resetHandlers()
30-
queryClient.clear()
33+
queryClientV5.clear()
3134
})
3235

3336
afterAll(() => {
Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
import { useMutation, useQueryClient } from '@tanstack/react-query'
1+
import {
2+
useMutation as useMutationV5,
3+
useQueryClient as useQueryClientV5,
4+
} from '@tanstack/react-queryV5'
25
import { z } from 'zod'
36

47
import Api from 'shared/api'
5-
import { NetworkErrorObject, rejectNetworkError } from 'shared/api/helpers'
8+
import { rejectNetworkError } from 'shared/api/helpers'
69

710
import { USER_TOKEN_TYPE } from './constants'
11+
import { SessionsQueryOpts } from './SessionsQueryOpts'
812

913
const UseGenerateTokenResponseSchema = z.object({
1014
createUserToken: z
1115
.object({
16+
fullToken: z.string().nullable(),
1217
error: z
1318
.discriminatedUnion('__typename', [
1419
z.object({
@@ -21,45 +26,47 @@ const UseGenerateTokenResponseSchema = z.object({
2126
}),
2227
])
2328
.nullable(),
24-
fullToken: z.string().nullable(),
2529
})
2630
.nullable(),
2731
})
2832

33+
const query = `mutation CreateUserToken($input: CreateUserTokenInput!) {
34+
createUserToken(input: $input) {
35+
fullToken
36+
error {
37+
__typename
38+
}
39+
}
40+
}`
41+
2942
export function useGenerateUserToken({ provider }: { provider: string }) {
30-
const queryClient = useQueryClient()
31-
return useMutation({
43+
const queryClientV5 = useQueryClientV5()
44+
return useMutationV5({
3245
mutationFn: ({ name }: { name: string }) => {
33-
const query = `
34-
mutation CreateUserToken($input: CreateUserTokenInput!) {
35-
createUserToken(input: $input) {
36-
error {
37-
__typename
38-
}
39-
fullToken
40-
}
41-
}
42-
`
4346
const variables = { input: { name, tokenType: USER_TOKEN_TYPE.API } }
4447
return Api.graphqlMutation({
4548
provider,
4649
query,
4750
variables,
4851
mutationPath: 'createUserToken',
52+
}).then((res) => {
53+
const parsedData = UseGenerateTokenResponseSchema.safeParse(res?.data)
54+
if (!parsedData.success) {
55+
return rejectNetworkError({
56+
status: 404,
57+
data: {},
58+
dev: 'useGenerateUserToken - 404 failed to parse',
59+
})
60+
}
61+
62+
return parsedData.data
4963
})
5064
},
51-
useErrorBoundary: true,
52-
onSuccess: ({ data }) => {
53-
queryClient.invalidateQueries(['sessions'])
54-
55-
const parsedData = UseGenerateTokenResponseSchema.safeParse(data)
56-
if (!parsedData.success) {
57-
return rejectNetworkError({
58-
status: 404,
59-
data: {},
60-
dev: 'useGenerateUserToken - 404 failed to parse',
61-
} satisfies NetworkErrorObject)
62-
}
65+
throwOnError: true,
66+
onSuccess: () => {
67+
queryClientV5.invalidateQueries({
68+
queryKey: SessionsQueryOpts({ provider }).queryKey,
69+
})
6370
},
6471
})
6572
}

0 commit comments

Comments
 (0)