@sanity/sdk-react 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -3
- package/dist/index.js +92 -19
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
- package/src/components/auth/AuthBoundary.test.tsx +33 -20
- package/src/components/auth/AuthBoundary.tsx +11 -1
- package/src/components/auth/LoginError.tsx +9 -12
- package/src/components/errors/CorsErrorComponent.test.tsx +48 -0
- package/src/components/errors/CorsErrorComponent.tsx +37 -0
- package/src/components/errors/Error.styles.ts +35 -0
- package/src/components/errors/Error.tsx +40 -0
- package/src/context/ComlinkTokenRefresh.test.tsx +53 -40
- package/src/hooks/auth/useDashboardOrganizationId.test.tsx +16 -7
- package/src/hooks/auth/useVerifyOrgProjects.test.tsx +56 -14
- package/src/hooks/dashboard/{useManageFavorite.test.ts → useManageFavorite.test.tsx} +99 -44
- package/src/hooks/document/{useDocument.test.ts → useDocument.test.tsx} +25 -22
- package/src/hooks/document/{useDocumentEvent.test.ts → useDocumentEvent.test.tsx} +17 -16
- package/src/hooks/document/{useDocumentPermissions.test.ts → useDocumentPermissions.test.tsx} +101 -40
- package/src/hooks/document/{useEditDocument.test.ts → useEditDocument.test.tsx} +52 -22
- package/src/hooks/documents/useDocuments.test.tsx +63 -25
- package/src/hooks/helpers/createCallbackHook.test.tsx +41 -37
- package/src/hooks/paginatedDocuments/usePaginatedDocuments.test.tsx +2 -2
- package/src/hooks/presence/usePresence.test.tsx +9 -6
- package/src/hooks/preview/useDocumentPreview.test.tsx +15 -16
- package/src/hooks/projection/useDocumentProjection.test.tsx +23 -38
- package/src/hooks/projection/useDocumentProjection.ts +3 -8
- package/src/hooks/query/useQuery.test.tsx +18 -10
- package/src/hooks/releases/useActiveReleases.test.tsx +25 -21
- package/src/hooks/releases/usePerspective.test.tsx +16 -22
- package/src/hooks/users/useUser.test.tsx +32 -15
- package/src/hooks/users/useUsers.test.tsx +19 -11
- package/src/hooks/_synchronous-groq-js.mjs +0 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/sdk-react",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Sanity SDK React toolkit for Content OS",
|
|
6
6
|
"keywords": [
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"browserslist": "extends @sanity/browserslist-config",
|
|
43
43
|
"prettier": "@sanity/prettier-config",
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@sanity/client": "^7.
|
|
45
|
+
"@sanity/client": "^7.12.0",
|
|
46
46
|
"@sanity/message-protocol": "^0.12.0",
|
|
47
47
|
"@sanity/types": "^3.83.0",
|
|
48
48
|
"@types/lodash-es": "^4.17.12",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"react-compiler-runtime": "19.1.0-rc.2",
|
|
52
52
|
"react-error-boundary": "^5.0.0",
|
|
53
53
|
"rxjs": "^7.8.2",
|
|
54
|
-
"@sanity/sdk": "2.
|
|
54
|
+
"@sanity/sdk": "2.3.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@sanity/browserslist-config": "^1.0.5",
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"@vitest/coverage-v8": "3.1.2",
|
|
67
67
|
"babel-plugin-react-compiler": "19.1.0-rc.1",
|
|
68
68
|
"eslint": "^9.22.0",
|
|
69
|
+
"groq-js": "^1.19.0",
|
|
69
70
|
"jsdom": "^25.0.1",
|
|
70
71
|
"prettier": "^3.5.3",
|
|
71
72
|
"react": "^19.1.0",
|
|
@@ -77,8 +78,8 @@
|
|
|
77
78
|
"@repo/config-eslint": "0.0.0",
|
|
78
79
|
"@repo/config-test": "0.0.1",
|
|
79
80
|
"@repo/package.bundle": "3.82.0",
|
|
80
|
-
"@repo/
|
|
81
|
-
"@repo/
|
|
81
|
+
"@repo/package.config": "0.0.1",
|
|
82
|
+
"@repo/tsconfig": "0.0.1"
|
|
82
83
|
},
|
|
83
84
|
"peerDependencies": {
|
|
84
85
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -8,7 +8,6 @@ import {ResourceProvider} from '../../context/ResourceProvider'
|
|
|
8
8
|
import {useAuthState} from '../../hooks/auth/useAuthState'
|
|
9
9
|
import {useLoginUrl} from '../../hooks/auth/useLoginUrl'
|
|
10
10
|
import {useVerifyOrgProjects} from '../../hooks/auth/useVerifyOrgProjects'
|
|
11
|
-
import {useSanityInstance} from '../../hooks/context/useSanityInstance'
|
|
12
11
|
import {AuthBoundary} from './AuthBoundary'
|
|
13
12
|
|
|
14
13
|
// Mock hooks
|
|
@@ -23,9 +22,6 @@ vi.mock('../../hooks/auth/useHandleAuthCallback', () => ({
|
|
|
23
22
|
vi.mock('../../hooks/auth/useLogOut', () => ({
|
|
24
23
|
useLogOut: vi.fn(() => async () => {}),
|
|
25
24
|
}))
|
|
26
|
-
vi.mock('../../hooks/context/useSanityInstance', () => ({
|
|
27
|
-
useSanityInstance: vi.fn(),
|
|
28
|
-
}))
|
|
29
25
|
|
|
30
26
|
// Mock AuthError throwing scenario
|
|
31
27
|
vi.mock('./AuthError', async (importOriginal) => {
|
|
@@ -109,7 +105,6 @@ describe('AuthBoundary', () => {
|
|
|
109
105
|
const mockUseAuthState = vi.mocked(useAuthState)
|
|
110
106
|
const mockUseLoginUrl = vi.mocked(useLoginUrl)
|
|
111
107
|
const mockUseVerifyOrgProjects = vi.mocked(useVerifyOrgProjects)
|
|
112
|
-
const mockUseSanityInstance = vi.mocked(useSanityInstance)
|
|
113
108
|
const testProjectIds = ['proj-test'] // Example project ID for tests
|
|
114
109
|
|
|
115
110
|
// Mock Sanity instance
|
|
@@ -139,8 +134,6 @@ describe('AuthBoundary', () => {
|
|
|
139
134
|
mockUseLoginUrl.mockReturnValue('http://example.com/login')
|
|
140
135
|
// Default mock for useVerifyOrgProjects - returns null (no error)
|
|
141
136
|
mockUseVerifyOrgProjects.mockImplementation(() => null)
|
|
142
|
-
// Mock useSanityInstance to return our mock instance
|
|
143
|
-
mockUseSanityInstance.mockReturnValue(mockSanityInstance)
|
|
144
137
|
})
|
|
145
138
|
|
|
146
139
|
afterEach(() => {
|
|
@@ -170,7 +163,9 @@ describe('AuthBoundary', () => {
|
|
|
170
163
|
isExchangingToken: false,
|
|
171
164
|
})
|
|
172
165
|
const {container} = render(
|
|
173
|
-
<
|
|
166
|
+
<ResourceProvider projectId="p" dataset="d" fallback={null}>
|
|
167
|
+
<AuthBoundary projectIds={testProjectIds}>Protected Content</AuthBoundary>
|
|
168
|
+
</ResourceProvider>,
|
|
174
169
|
)
|
|
175
170
|
|
|
176
171
|
// The callback screen renders null check that it renders nothing
|
|
@@ -184,7 +179,11 @@ describe('AuthBoundary', () => {
|
|
|
184
179
|
currentUser: null,
|
|
185
180
|
token: 'exampleToken',
|
|
186
181
|
})
|
|
187
|
-
render(
|
|
182
|
+
render(
|
|
183
|
+
<ResourceProvider projectId="p" dataset="d" fallback={null}>
|
|
184
|
+
<AuthBoundary projectIds={testProjectIds}>Protected Content</AuthBoundary>
|
|
185
|
+
</ResourceProvider>,
|
|
186
|
+
)
|
|
188
187
|
|
|
189
188
|
expect(screen.getByText('Protected Content')).toBeInTheDocument()
|
|
190
189
|
})
|
|
@@ -194,7 +193,11 @@ describe('AuthBoundary', () => {
|
|
|
194
193
|
type: AuthStateType.ERROR,
|
|
195
194
|
error: new Error('test error'),
|
|
196
195
|
})
|
|
197
|
-
render(
|
|
196
|
+
render(
|
|
197
|
+
<ResourceProvider projectId="p" dataset="d" fallback={null}>
|
|
198
|
+
<AuthBoundary projectIds={testProjectIds}>Protected Content</AuthBoundary>
|
|
199
|
+
</ResourceProvider>,
|
|
200
|
+
)
|
|
198
201
|
|
|
199
202
|
// The AuthBoundary should throw an AuthError internally
|
|
200
203
|
// and then display the LoginError component as the fallback.
|
|
@@ -207,7 +210,11 @@ describe('AuthBoundary', () => {
|
|
|
207
210
|
})
|
|
208
211
|
|
|
209
212
|
it('renders children when logged in and org verification passes', () => {
|
|
210
|
-
render(
|
|
213
|
+
render(
|
|
214
|
+
<ResourceProvider projectId="p" dataset="d" fallback={null}>
|
|
215
|
+
<AuthBoundary projectIds={testProjectIds}>Protected Content</AuthBoundary>
|
|
216
|
+
</ResourceProvider>,
|
|
217
|
+
)
|
|
211
218
|
expect(screen.getByText('Protected Content')).toBeInTheDocument()
|
|
212
219
|
})
|
|
213
220
|
|
|
@@ -226,9 +233,11 @@ describe('AuthBoundary', () => {
|
|
|
226
233
|
|
|
227
234
|
// Need to catch the error thrown during render. ErrorBoundary mock handles this.
|
|
228
235
|
render(
|
|
229
|
-
<
|
|
230
|
-
<
|
|
231
|
-
|
|
236
|
+
<ResourceProvider projectId="p" dataset="d" fallback={null}>
|
|
237
|
+
<AuthBoundary verifyOrganization={true} projectIds={testProjectIds}>
|
|
238
|
+
<div>Protected Content</div>
|
|
239
|
+
</AuthBoundary>
|
|
240
|
+
</ResourceProvider>,
|
|
232
241
|
)
|
|
233
242
|
|
|
234
243
|
// The ErrorBoundary's FallbackComponent should be rendered
|
|
@@ -256,9 +265,11 @@ describe('AuthBoundary', () => {
|
|
|
256
265
|
})
|
|
257
266
|
|
|
258
267
|
render(
|
|
259
|
-
<
|
|
260
|
-
<
|
|
261
|
-
|
|
268
|
+
<ResourceProvider projectId="p" dataset="d" fallback={null}>
|
|
269
|
+
<AuthBoundary verifyOrganization={false} projectIds={testProjectIds}>
|
|
270
|
+
<div>Protected Content</div>
|
|
271
|
+
</AuthBoundary>
|
|
272
|
+
</ResourceProvider>,
|
|
262
273
|
)
|
|
263
274
|
|
|
264
275
|
// Should render children because verification is disabled
|
|
@@ -279,9 +290,11 @@ describe('AuthBoundary', () => {
|
|
|
279
290
|
mockUseVerifyOrgProjects.mockImplementation(() => null)
|
|
280
291
|
|
|
281
292
|
render(
|
|
282
|
-
<
|
|
283
|
-
<
|
|
284
|
-
|
|
293
|
+
<ResourceProvider projectId="p" dataset="d" fallback={null}>
|
|
294
|
+
<AuthBoundary projectIds={testProjectIds}>
|
|
295
|
+
<div>Protected Content</div>
|
|
296
|
+
</AuthBoundary>
|
|
297
|
+
</ResourceProvider>,
|
|
285
298
|
)
|
|
286
299
|
|
|
287
300
|
await waitFor(() => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {CorsOriginError} from '@sanity/client'
|
|
2
|
+
import {AuthStateType, getCorsErrorProjectId} from '@sanity/sdk'
|
|
2
3
|
import {useEffect, useMemo} from 'react'
|
|
3
4
|
import {ErrorBoundary, type FallbackProps} from 'react-error-boundary'
|
|
4
5
|
|
|
@@ -6,6 +7,7 @@ import {ComlinkTokenRefreshProvider} from '../../context/ComlinkTokenRefresh'
|
|
|
6
7
|
import {useAuthState} from '../../hooks/auth/useAuthState'
|
|
7
8
|
import {useLoginUrl} from '../../hooks/auth/useLoginUrl'
|
|
8
9
|
import {useVerifyOrgProjects} from '../../hooks/auth/useVerifyOrgProjects'
|
|
10
|
+
import {CorsErrorComponent} from '../errors/CorsErrorComponent'
|
|
9
11
|
import {isInIframe} from '../utils'
|
|
10
12
|
import {AuthError} from './AuthError'
|
|
11
13
|
import {ConfigurationError} from './ConfigurationError'
|
|
@@ -107,6 +109,14 @@ export function AuthBoundary({
|
|
|
107
109
|
}: AuthBoundaryProps): React.ReactNode {
|
|
108
110
|
const FallbackComponent = useMemo(() => {
|
|
109
111
|
return function LoginComponentWithLayoutProps(fallbackProps: FallbackProps) {
|
|
112
|
+
if (fallbackProps.error instanceof CorsOriginError) {
|
|
113
|
+
return (
|
|
114
|
+
<CorsErrorComponent
|
|
115
|
+
{...fallbackProps}
|
|
116
|
+
projectId={getCorsErrorProjectId(fallbackProps.error)}
|
|
117
|
+
/>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
110
120
|
return <LoginErrorComponent {...fallbackProps} />
|
|
111
121
|
}
|
|
112
122
|
}, [LoginErrorComponent])
|
|
@@ -5,6 +5,7 @@ import {type FallbackProps} from 'react-error-boundary'
|
|
|
5
5
|
|
|
6
6
|
import {useAuthState} from '../../hooks/auth/useAuthState'
|
|
7
7
|
import {useLogOut} from '../../hooks/auth/useLogOut'
|
|
8
|
+
import {Error} from '../errors/Error'
|
|
8
9
|
import {AuthError} from './AuthError'
|
|
9
10
|
import {ConfigurationError} from './ConfigurationError'
|
|
10
11
|
/**
|
|
@@ -59,17 +60,13 @@ export function LoginError({error, resetErrorBoundary}: LoginErrorProps): React.
|
|
|
59
60
|
}, [authState, handleRetry, error])
|
|
60
61
|
|
|
61
62
|
return (
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<button className="sc-login-error__button" onClick={handleRetry}>
|
|
71
|
-
Retry
|
|
72
|
-
</button>
|
|
73
|
-
</div>
|
|
63
|
+
<Error
|
|
64
|
+
heading={error instanceof AuthError ? 'Authentication Error' : 'Configuration Error'}
|
|
65
|
+
description={authErrorMessage}
|
|
66
|
+
cta={{
|
|
67
|
+
text: 'Retry',
|
|
68
|
+
onClick: handleRetry,
|
|
69
|
+
}}
|
|
70
|
+
/>
|
|
74
71
|
)
|
|
75
72
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {describe, expect, it} from 'vitest'
|
|
2
|
+
|
|
3
|
+
import {render, screen} from '../../../test/test-utils'
|
|
4
|
+
import {CorsErrorComponent} from './CorsErrorComponent'
|
|
5
|
+
|
|
6
|
+
describe('CorsErrorComponent', () => {
|
|
7
|
+
it('shows origin and manage link when projectId is provided', () => {
|
|
8
|
+
const origin = 'https://example.com'
|
|
9
|
+
const originalLocation = window.location
|
|
10
|
+
// Redefine window.location to control origin in this test
|
|
11
|
+
Object.defineProperty(window, 'location', {
|
|
12
|
+
value: {origin},
|
|
13
|
+
configurable: true,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
render(
|
|
17
|
+
<CorsErrorComponent
|
|
18
|
+
projectId="proj123"
|
|
19
|
+
error={new Error('nope')}
|
|
20
|
+
resetErrorBoundary={() => {}}
|
|
21
|
+
/>,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
expect(screen.getByText('Before you continue…')).toBeInTheDocument()
|
|
25
|
+
expect(screen.getByText(origin)).toBeInTheDocument()
|
|
26
|
+
|
|
27
|
+
const link = screen.getByRole('link', {name: 'Manage CORS configuration'}) as HTMLAnchorElement
|
|
28
|
+
expect(link).toBeInTheDocument()
|
|
29
|
+
expect(link.target).toBe('_blank')
|
|
30
|
+
expect(link.rel).toContain('noopener')
|
|
31
|
+
expect(link.href).toContain('https://sanity.io/manage/project/proj123/api')
|
|
32
|
+
expect(link.href).toContain('cors=add')
|
|
33
|
+
expect(link.href).toContain(`origin=${encodeURIComponent(origin)}`)
|
|
34
|
+
expect(link.href).toContain('credentials=include')
|
|
35
|
+
|
|
36
|
+
// restore
|
|
37
|
+
Object.defineProperty(window, 'location', {value: originalLocation})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('shows error message when projectId is null', () => {
|
|
41
|
+
const error = new Error('some error message')
|
|
42
|
+
render(<CorsErrorComponent projectId={null} error={error} resetErrorBoundary={() => {}} />)
|
|
43
|
+
|
|
44
|
+
expect(screen.getByText('Before you continue…')).toBeInTheDocument()
|
|
45
|
+
expect(screen.getByText('some error message')).toBeInTheDocument()
|
|
46
|
+
expect(screen.queryByRole('link', {name: 'Manage CORS configuration'})).toBeNull()
|
|
47
|
+
})
|
|
48
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {useMemo} from 'react'
|
|
2
|
+
import {type FallbackProps} from 'react-error-boundary'
|
|
3
|
+
|
|
4
|
+
import {Error} from './Error'
|
|
5
|
+
|
|
6
|
+
type CorsErrorComponentProps = FallbackProps & {
|
|
7
|
+
projectId: string | null
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function CorsErrorComponent({projectId, error}: CorsErrorComponentProps): React.ReactNode {
|
|
11
|
+
const origin = window.location.origin
|
|
12
|
+
const corsUrl = useMemo(() => {
|
|
13
|
+
const url = new URL(`https://sanity.io/manage/project/${projectId}/api`)
|
|
14
|
+
url.searchParams.set('cors', 'add')
|
|
15
|
+
url.searchParams.set('origin', origin)
|
|
16
|
+
url.searchParams.set('credentials', 'include')
|
|
17
|
+
return url.toString()
|
|
18
|
+
}, [origin, projectId])
|
|
19
|
+
return (
|
|
20
|
+
<Error
|
|
21
|
+
heading="Before you continue…"
|
|
22
|
+
{...(projectId
|
|
23
|
+
? {
|
|
24
|
+
description:
|
|
25
|
+
'To access your content, you need to <strong>add the following URL as a CORS origin</strong> to your Sanity project.',
|
|
26
|
+
code: origin,
|
|
27
|
+
cta: {
|
|
28
|
+
text: 'Manage CORS configuration',
|
|
29
|
+
href: corsUrl,
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
: {
|
|
33
|
+
description: error?.message,
|
|
34
|
+
})}
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const FONT_SANS_SERIF = `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, system-ui, sans-serif`
|
|
2
|
+
const FONT_MONOSPACE = `-apple-system-ui-monospace, 'SF Mono', Menlo, Monaco, Consolas, monospace`
|
|
3
|
+
|
|
4
|
+
const styles: Record<string, React.CSSProperties> = {
|
|
5
|
+
container: {
|
|
6
|
+
padding: '28px',
|
|
7
|
+
fontFamily: FONT_SANS_SERIF,
|
|
8
|
+
display: 'flex',
|
|
9
|
+
flexDirection: 'column',
|
|
10
|
+
gap: '21px',
|
|
11
|
+
fontSize: '14px',
|
|
12
|
+
},
|
|
13
|
+
heading: {
|
|
14
|
+
margin: 0,
|
|
15
|
+
fontSize: '28px',
|
|
16
|
+
fontWeight: 700,
|
|
17
|
+
},
|
|
18
|
+
paragraph: {
|
|
19
|
+
margin: 0,
|
|
20
|
+
},
|
|
21
|
+
link: {
|
|
22
|
+
appearance: 'none',
|
|
23
|
+
background: 'transparent',
|
|
24
|
+
border: 0,
|
|
25
|
+
padding: 0,
|
|
26
|
+
font: 'inherit',
|
|
27
|
+
textDecoration: 'underline',
|
|
28
|
+
cursor: 'pointer',
|
|
29
|
+
},
|
|
30
|
+
code: {
|
|
31
|
+
fontFamily: FONT_MONOSPACE,
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default styles
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import styles from './Error.styles'
|
|
2
|
+
|
|
3
|
+
type ErrorProps = {
|
|
4
|
+
heading: string
|
|
5
|
+
description?: string
|
|
6
|
+
code?: string
|
|
7
|
+
cta?: {
|
|
8
|
+
text: string
|
|
9
|
+
href?: string
|
|
10
|
+
onClick?: () => void
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function Error({heading, description, code, cta}: ErrorProps): React.ReactNode {
|
|
15
|
+
return (
|
|
16
|
+
<div style={styles['container']}>
|
|
17
|
+
<h1 style={styles['heading']}>{heading}</h1>
|
|
18
|
+
|
|
19
|
+
{description && (
|
|
20
|
+
<p style={styles['paragraph']} dangerouslySetInnerHTML={{__html: description}} />
|
|
21
|
+
)}
|
|
22
|
+
|
|
23
|
+
{code && <code style={styles['code']}>{code}</code>}
|
|
24
|
+
|
|
25
|
+
{cta && (cta.href || cta.onClick) && (
|
|
26
|
+
<p style={styles['paragraph']}>
|
|
27
|
+
{cta.href ? (
|
|
28
|
+
<a style={styles['link']} href={cta.href} target="_blank" rel="noopener noreferrer">
|
|
29
|
+
{cta.text}
|
|
30
|
+
</a>
|
|
31
|
+
) : (
|
|
32
|
+
<button style={styles['link']} onClick={cta.onClick}>
|
|
33
|
+
{cta.text}
|
|
34
|
+
</button>
|
|
35
|
+
)}
|
|
36
|
+
</p>
|
|
37
|
+
)}
|
|
38
|
+
</div>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
@@ -5,8 +5,8 @@ import {afterEach, beforeEach, describe, expect, it, type Mock, vi} from 'vitest
|
|
|
5
5
|
|
|
6
6
|
import {useAuthState} from '../hooks/auth/useAuthState'
|
|
7
7
|
import {useWindowConnection} from '../hooks/comlink/useWindowConnection'
|
|
8
|
-
import {useSanityInstance} from '../hooks/context/useSanityInstance'
|
|
9
8
|
import {ComlinkTokenRefreshProvider} from './ComlinkTokenRefresh'
|
|
9
|
+
import {ResourceProvider} from './ResourceProvider'
|
|
10
10
|
|
|
11
11
|
// Mocks
|
|
12
12
|
vi.mock('@sanity/sdk', async () => {
|
|
@@ -26,20 +26,13 @@ vi.mock('../hooks/comlink/useWindowConnection', () => ({
|
|
|
26
26
|
useWindowConnection: vi.fn(),
|
|
27
27
|
}))
|
|
28
28
|
|
|
29
|
-
vi.mock('../hooks/context/useSanityInstance', () => ({
|
|
30
|
-
useSanityInstance: vi.fn(),
|
|
31
|
-
}))
|
|
32
|
-
|
|
33
29
|
// Use simpler mock typings
|
|
34
30
|
const mockGetIsInDashboardState = getIsInDashboardState as Mock
|
|
35
31
|
const mockSetAuthToken = setAuthToken as Mock
|
|
36
32
|
const mockUseAuthState = useAuthState as Mock
|
|
37
33
|
const mockUseWindowConnection = useWindowConnection as Mock
|
|
38
|
-
const mockUseSanityInstance = useSanityInstance as Mock
|
|
39
34
|
|
|
40
35
|
const mockFetch = vi.fn()
|
|
41
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
-
const mockSanityInstance: any = {projectId: 'test', dataset: 'test'}
|
|
43
36
|
|
|
44
37
|
describe('ComlinkTokenRefresh', () => {
|
|
45
38
|
beforeEach(() => {
|
|
@@ -47,7 +40,6 @@ describe('ComlinkTokenRefresh', () => {
|
|
|
47
40
|
mockGetIsInDashboardState.mockReturnValue({getCurrent: vi.fn(() => false)})
|
|
48
41
|
mockUseAuthState.mockReturnValue({type: AuthStateType.LOGGED_IN})
|
|
49
42
|
mockUseWindowConnection.mockReturnValue({fetch: mockFetch})
|
|
50
|
-
mockUseSanityInstance.mockReturnValue(mockSanityInstance)
|
|
51
43
|
})
|
|
52
44
|
|
|
53
45
|
afterEach(() => {
|
|
@@ -64,9 +56,11 @@ describe('ComlinkTokenRefresh', () => {
|
|
|
64
56
|
it('should not request new token on 401 if not in dashboard', async () => {
|
|
65
57
|
mockUseAuthState.mockReturnValue({type: AuthStateType.LOGGED_IN})
|
|
66
58
|
const {rerender} = render(
|
|
67
|
-
<
|
|
68
|
-
<
|
|
69
|
-
|
|
59
|
+
<ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
|
|
60
|
+
<ComlinkTokenRefreshProvider>
|
|
61
|
+
<div>Test</div>
|
|
62
|
+
</ComlinkTokenRefreshProvider>
|
|
63
|
+
</ResourceProvider>,
|
|
70
64
|
)
|
|
71
65
|
|
|
72
66
|
mockUseAuthState.mockReturnValue({
|
|
@@ -75,9 +69,11 @@ describe('ComlinkTokenRefresh', () => {
|
|
|
75
69
|
})
|
|
76
70
|
act(() => {
|
|
77
71
|
rerender(
|
|
78
|
-
<
|
|
79
|
-
<
|
|
80
|
-
|
|
72
|
+
<ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
|
|
73
|
+
<ComlinkTokenRefreshProvider>
|
|
74
|
+
<div>Test</div>
|
|
75
|
+
</ComlinkTokenRefreshProvider>
|
|
76
|
+
</ResourceProvider>,
|
|
81
77
|
)
|
|
82
78
|
})
|
|
83
79
|
|
|
@@ -95,9 +91,11 @@ describe('ComlinkTokenRefresh', () => {
|
|
|
95
91
|
|
|
96
92
|
it('should initialize useWindowConnection with correct parameters', () => {
|
|
97
93
|
render(
|
|
98
|
-
<
|
|
99
|
-
<
|
|
100
|
-
|
|
94
|
+
<ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
|
|
95
|
+
<ComlinkTokenRefreshProvider>
|
|
96
|
+
<div>Test</div>
|
|
97
|
+
</ComlinkTokenRefreshProvider>
|
|
98
|
+
</ResourceProvider>,
|
|
101
99
|
)
|
|
102
100
|
|
|
103
101
|
expect(mockUseWindowConnection).toHaveBeenCalledWith(
|
|
@@ -116,16 +114,18 @@ describe('ComlinkTokenRefresh', () => {
|
|
|
116
114
|
mockFetch.mockResolvedValueOnce({token: 'new-token'})
|
|
117
115
|
|
|
118
116
|
render(
|
|
119
|
-
<
|
|
120
|
-
<
|
|
121
|
-
|
|
117
|
+
<ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
|
|
118
|
+
<ComlinkTokenRefreshProvider>
|
|
119
|
+
<div>Test</div>
|
|
120
|
+
</ComlinkTokenRefreshProvider>
|
|
121
|
+
</ResourceProvider>,
|
|
122
122
|
)
|
|
123
123
|
|
|
124
124
|
await act(async () => {
|
|
125
125
|
await vi.advanceTimersByTimeAsync(100)
|
|
126
126
|
})
|
|
127
127
|
|
|
128
|
-
expect(mockSetAuthToken).toHaveBeenCalledWith(
|
|
128
|
+
expect(mockSetAuthToken).toHaveBeenCalledWith(expect.any(Object), 'new-token')
|
|
129
129
|
expect(mockFetch).toHaveBeenCalledTimes(1)
|
|
130
130
|
})
|
|
131
131
|
|
|
@@ -137,9 +137,11 @@ describe('ComlinkTokenRefresh', () => {
|
|
|
137
137
|
mockFetch.mockResolvedValueOnce({token: null})
|
|
138
138
|
|
|
139
139
|
render(
|
|
140
|
-
<
|
|
141
|
-
<
|
|
142
|
-
|
|
140
|
+
<ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
|
|
141
|
+
<ComlinkTokenRefreshProvider>
|
|
142
|
+
<div>Test</div>
|
|
143
|
+
</ComlinkTokenRefreshProvider>
|
|
144
|
+
</ResourceProvider>,
|
|
143
145
|
)
|
|
144
146
|
|
|
145
147
|
await act(async () => {
|
|
@@ -157,9 +159,11 @@ describe('ComlinkTokenRefresh', () => {
|
|
|
157
159
|
mockFetch.mockRejectedValueOnce(new Error('Fetch failed'))
|
|
158
160
|
|
|
159
161
|
render(
|
|
160
|
-
<
|
|
161
|
-
<
|
|
162
|
-
|
|
162
|
+
<ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
|
|
163
|
+
<ComlinkTokenRefreshProvider>
|
|
164
|
+
<div>Test</div>
|
|
165
|
+
</ComlinkTokenRefreshProvider>
|
|
166
|
+
</ResourceProvider>,
|
|
163
167
|
)
|
|
164
168
|
|
|
165
169
|
await act(async () => {
|
|
@@ -173,9 +177,12 @@ describe('ComlinkTokenRefresh', () => {
|
|
|
173
177
|
it('should not request new token for non-401 errors', async () => {
|
|
174
178
|
mockUseAuthState.mockReturnValue({type: AuthStateType.LOGGED_IN})
|
|
175
179
|
const {rerender} = render(
|
|
176
|
-
<
|
|
177
|
-
<
|
|
178
|
-
|
|
180
|
+
<ResourceProvider fallback={null}>
|
|
181
|
+
<ComlinkTokenRefreshProvider>
|
|
182
|
+
<div>Test</div>
|
|
183
|
+
</ComlinkTokenRefreshProvider>
|
|
184
|
+
,
|
|
185
|
+
</ResourceProvider>,
|
|
179
186
|
)
|
|
180
187
|
|
|
181
188
|
mockUseAuthState.mockReturnValue({
|
|
@@ -184,9 +191,11 @@ describe('ComlinkTokenRefresh', () => {
|
|
|
184
191
|
})
|
|
185
192
|
act(() => {
|
|
186
193
|
rerender(
|
|
187
|
-
<
|
|
188
|
-
<
|
|
189
|
-
|
|
194
|
+
<ResourceProvider fallback={null}>
|
|
195
|
+
<ComlinkTokenRefreshProvider>
|
|
196
|
+
<div>Test</div>
|
|
197
|
+
</ComlinkTokenRefreshProvider>
|
|
198
|
+
</ResourceProvider>,
|
|
190
199
|
)
|
|
191
200
|
})
|
|
192
201
|
|
|
@@ -199,17 +208,21 @@ describe('ComlinkTokenRefresh', () => {
|
|
|
199
208
|
it('should request new token on LOGGED_OUT state', async () => {
|
|
200
209
|
mockUseAuthState.mockReturnValue({type: AuthStateType.LOGGED_IN})
|
|
201
210
|
const {rerender} = render(
|
|
202
|
-
<
|
|
203
|
-
<
|
|
204
|
-
|
|
211
|
+
<ResourceProvider fallback={null}>
|
|
212
|
+
<ComlinkTokenRefreshProvider>
|
|
213
|
+
<div>Test</div>
|
|
214
|
+
</ComlinkTokenRefreshProvider>
|
|
215
|
+
</ResourceProvider>,
|
|
205
216
|
)
|
|
206
217
|
|
|
207
218
|
mockUseAuthState.mockReturnValue({type: AuthStateType.LOGGED_OUT})
|
|
208
219
|
act(() => {
|
|
209
220
|
rerender(
|
|
210
|
-
<
|
|
211
|
-
<
|
|
212
|
-
|
|
221
|
+
<ResourceProvider fallback={null}>
|
|
222
|
+
<ComlinkTokenRefreshProvider>
|
|
223
|
+
<div>Test</div>
|
|
224
|
+
</ComlinkTokenRefreshProvider>
|
|
225
|
+
</ResourceProvider>,
|
|
213
226
|
)
|
|
214
227
|
})
|
|
215
228
|
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {getDashboardOrganizationId} from '@sanity/sdk'
|
|
2
2
|
import {renderHook} from '@testing-library/react'
|
|
3
3
|
import {throwError} from 'rxjs'
|
|
4
4
|
import {describe, expect, it, vi} from 'vitest'
|
|
5
5
|
|
|
6
|
+
import {ResourceProvider} from '../../context/ResourceProvider'
|
|
6
7
|
import {useDashboardOrganizationId} from './useDashboardOrganizationId'
|
|
7
8
|
|
|
8
|
-
vi.mock('../context/useSanityInstance', () => ({
|
|
9
|
-
useSanityInstance: vi.fn().mockReturnValue(createSanityInstance({projectId: 'p', dataset: 'd'})),
|
|
10
|
-
}))
|
|
11
|
-
|
|
12
9
|
vi.mock('@sanity/sdk', async (importOriginal) => {
|
|
13
10
|
const actual = await importOriginal()
|
|
14
11
|
return {...(actual || {}), getDashboardOrganizationId: vi.fn()}
|
|
@@ -23,7 +20,13 @@ describe('useDashboardOrganizationId', () => {
|
|
|
23
20
|
observable: throwError(() => new Error('Unexpected usage of observable')),
|
|
24
21
|
})
|
|
25
22
|
|
|
26
|
-
const {result} = renderHook(() => useDashboardOrganizationId()
|
|
23
|
+
const {result} = renderHook(() => useDashboardOrganizationId(), {
|
|
24
|
+
wrapper: ({children}) => (
|
|
25
|
+
<ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
|
|
26
|
+
{children}
|
|
27
|
+
</ResourceProvider>
|
|
28
|
+
),
|
|
29
|
+
})
|
|
27
30
|
expect(result.current).toBeUndefined()
|
|
28
31
|
})
|
|
29
32
|
|
|
@@ -36,7 +39,13 @@ describe('useDashboardOrganizationId', () => {
|
|
|
36
39
|
observable: throwError(() => new Error('Unexpected usage of observable')),
|
|
37
40
|
})
|
|
38
41
|
|
|
39
|
-
const {result} = renderHook(() => useDashboardOrganizationId()
|
|
42
|
+
const {result} = renderHook(() => useDashboardOrganizationId(), {
|
|
43
|
+
wrapper: ({children}) => (
|
|
44
|
+
<ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
|
|
45
|
+
{children}
|
|
46
|
+
</ResourceProvider>
|
|
47
|
+
),
|
|
48
|
+
})
|
|
40
49
|
expect(result.current).toBe(mockOrgId)
|
|
41
50
|
})
|
|
42
51
|
})
|