@sanity/sdk 3.0.0-rc.0 → 3.0.0-rc.1
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/_chunks-dts/createGroqSearchFilter.d.ts +1147 -0
- package/dist/_chunks-dts/createGroqSearchFilter.d.ts.map +1 -0
- package/dist/_chunks-es/_internal.js +134 -0
- package/dist/_chunks-es/_internal.js.map +1 -0
- package/dist/_chunks-es/createGroqSearchFilter.js +1864 -0
- package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -0
- package/dist/_chunks-es/telemetryManager.js +93 -0
- package/dist/_chunks-es/telemetryManager.js.map +1 -0
- package/dist/_exports/_internal.d.ts +95 -0
- package/dist/_exports/_internal.d.ts.map +1 -0
- package/dist/_exports/_internal.js +24 -0
- package/dist/_exports/_internal.js.map +1 -0
- package/dist/index.d.ts +1079 -1076
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1967 -2302
- package/dist/index.js.map +1 -1
- package/package.json +48 -47
- package/src/_exports/_internal.ts +26 -0
- package/src/_exports/index.ts +120 -11
- package/src/access/checkPermissions.test.ts +55 -0
- package/src/access/checkPermissions.ts +54 -0
- package/src/agent/agentActions.test.ts +16 -60
- package/src/agent/agentActions.ts +25 -38
- package/src/applications/applications.test-d.ts +83 -0
- package/src/applications/applications.test.ts +130 -0
- package/src/applications/applications.ts +364 -0
- package/src/applications/userApplications.test.ts +126 -0
- package/src/applications/userApplications.ts +261 -0
- package/src/auth/authLogger.ts +30 -0
- package/src/auth/authMode.test.ts +25 -0
- package/src/auth/authMode.ts +6 -3
- package/src/auth/authStore.test.ts +194 -13
- package/src/auth/authStore.ts +59 -26
- package/src/auth/getOrganizationVerificationState.test.ts +18 -17
- package/src/auth/getOrganizationVerificationState.ts +4 -4
- package/src/auth/handleAuthCallback.test.ts +35 -1
- package/src/auth/handleAuthCallback.ts +25 -6
- package/src/auth/logout.test.ts +75 -2
- package/src/auth/logout.ts +23 -4
- package/src/auth/refreshStampedToken.test.ts +219 -469
- package/src/auth/refreshStampedToken.ts +79 -195
- package/src/auth/studioAuth.ts +1 -1
- package/src/auth/studioModeAuth.ts +2 -1
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +146 -13
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +37 -6
- package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +2 -0
- package/src/client/clientStore.test.ts +62 -37
- package/src/client/clientStore.ts +45 -75
- package/src/client/liveEvents.test.ts +151 -0
- package/src/client/liveEvents.ts +107 -0
- package/src/comlink/controller/actions/destroyController.test.ts +4 -1
- package/src/comlink/controller/actions/getOrCreateChannel.test.ts +4 -1
- package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
- package/src/comlink/controller/actions/getOrCreateController.test.ts +4 -1
- package/src/comlink/controller/actions/releaseChannel.test.ts +1 -1
- package/src/comlink/controller/comlinkControllerStore.test.ts +4 -1
- package/src/comlink/node/actions/getOrCreateNode.test.ts +9 -3
- package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
- package/src/comlink/node/actions/releaseNode.test.ts +7 -4
- package/src/comlink/node/comlinkNodeStore.test.ts +2 -2
- package/src/comlink/node/getNodeState.test.ts +1 -1
- package/src/comlink/node/getNodeState.ts +2 -1
- package/src/config/__tests__/handles.test.ts +18 -12
- package/src/config/handles.ts +25 -7
- package/src/config/sanityConfig.ts +109 -79
- package/src/datasets/datasets.test.ts +2 -2
- package/src/datasets/datasets.ts +10 -4
- package/src/document/actions.test.ts +115 -33
- package/src/document/actions.ts +212 -15
- package/src/document/applyDocumentActions.test.ts +38 -12
- package/src/document/applyDocumentActions.ts +30 -9
- package/src/document/documentConstants.ts +7 -0
- package/src/document/documentStore.concurrency.test.ts +1306 -0
- package/src/document/documentStore.test.ts +673 -278
- package/src/document/documentStore.ts +162 -71
- package/src/document/events.test.ts +57 -2
- package/src/document/events.ts +82 -26
- package/src/document/listen.test.ts +56 -0
- package/src/document/listen.ts +74 -21
- package/src/document/listenerEventOperators.test.ts +311 -0
- package/src/document/listenerEventOperators.ts +217 -0
- package/src/document/patchOperations.test.ts +49 -2
- package/src/document/patchOperations.ts +50 -0
- package/src/document/permissions.test.ts +105 -60
- package/src/document/permissions.ts +44 -19
- package/src/document/processActions/create.ts +138 -0
- package/src/document/processActions/delete.ts +100 -0
- package/src/document/processActions/discard.ts +63 -0
- package/src/document/processActions/edit.ts +154 -0
- package/src/document/processActions/processActions.ts +218 -0
- package/src/document/processActions/publish.ts +120 -0
- package/src/document/processActions/releaseArchive.ts +77 -0
- package/src/document/processActions/releaseCreate.ts +59 -0
- package/src/document/processActions/releaseDelete.ts +65 -0
- package/src/document/processActions/releaseEdit.ts +37 -0
- package/src/document/processActions/releasePublish.ts +45 -0
- package/src/document/processActions/releaseSchedule.ts +87 -0
- package/src/document/processActions/releaseUtil.ts +31 -0
- package/src/document/processActions/shared.ts +308 -0
- package/src/document/processActions/unpublish.ts +85 -0
- package/src/document/processActions.test.ts +1129 -121
- package/src/document/reducers.test.ts +639 -64
- package/src/document/reducers.ts +187 -48
- package/src/document/resourceRules.test.ts +178 -0
- package/src/document/resourceRules.ts +117 -0
- package/src/document/sharedListener.test.ts +16 -14
- package/src/document/sharedListener.ts +24 -8
- package/src/document/util.ts +14 -0
- package/src/favorites/favorites.test.ts +5 -6
- package/src/installations/installations.test-d.ts +68 -0
- package/src/installations/installations.test.ts +62 -0
- package/src/installations/installations.ts +202 -0
- package/src/organization/organization.test-d.ts +36 -0
- package/src/organization/organization.test.ts +138 -0
- package/src/organization/organization.ts +138 -0
- package/src/organizations/organizations.test-d.ts +56 -0
- package/src/organizations/organizations.test.ts +150 -0
- package/src/organizations/organizations.ts +107 -0
- package/src/presence/bifurTransport.test.ts +46 -6
- package/src/presence/bifurTransport.ts +19 -2
- package/src/presence/presenceStore.test.ts +94 -23
- package/src/presence/presenceStore.ts +85 -29
- package/src/preview/getPreviewState.test.ts +137 -0
- package/src/preview/getPreviewState.ts +69 -0
- package/src/preview/previewProjectionUtils.test.ts +9 -22
- package/src/preview/previewProjectionUtils.ts +7 -3
- package/src/preview/resolvePreview.test.ts +64 -0
- package/src/preview/resolvePreview.ts +43 -0
- package/src/preview/types.ts +10 -0
- package/src/project/project.test-d.ts +37 -0
- package/src/project/project.test.ts +157 -12
- package/src/project/project.ts +138 -25
- package/src/projection/getProjectionState.test.ts +18 -20
- package/src/projection/getProjectionState.ts +23 -3
- package/src/projection/projectionQuery.ts +5 -5
- package/src/projection/projectionStore.test.ts +1 -1
- package/src/projection/resolveProjection.test.ts +1 -2
- package/src/projection/resolveProjection.ts +16 -0
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
- package/src/projection/subscribeToStateAndFetchBatches.ts +32 -23
- package/src/projection/types.ts +8 -0
- package/src/projects/projects.test-d.ts +32 -0
- package/src/projects/projects.test.ts +108 -42
- package/src/projects/projects.ts +63 -27
- package/src/query/queryStore.test.ts +259 -123
- package/src/query/queryStore.ts +78 -63
- package/src/query/reducers.test.ts +0 -42
- package/src/query/reducers.ts +2 -18
- package/src/releases/getPerspectiveState.test.ts +17 -23
- package/src/releases/getPerspectiveState.ts +8 -20
- package/src/releases/observeReleases.test.ts +127 -0
- package/src/releases/observeReleases.ts +102 -0
- package/src/releases/releasesStore.test.ts +100 -53
- package/src/releases/releasesStore.ts +69 -44
- package/src/releases/utils/sortReleases.test.ts +38 -2
- package/src/releases/utils/sortReleases.ts +15 -10
- package/src/store/createActionBinder.test.ts +87 -90
- package/src/store/createActionBinder.ts +79 -25
- package/src/store/createSanityInstance.test.ts +83 -13
- package/src/store/createSanityInstance.ts +77 -2
- package/src/store/createStateSourceAction.test.ts +64 -2
- package/src/store/createStateSourceAction.ts +39 -44
- package/src/store/createStoreInstance.test.ts +4 -2
- package/src/store/fetcherStore.test-d.ts +121 -0
- package/src/store/fetcherStore.test.ts +299 -0
- package/src/store/fetcherStore.ts +801 -0
- package/src/telemetry/environment.test.ts +119 -0
- package/src/telemetry/environment.ts +92 -0
- package/src/telemetry/events.ts +42 -0
- package/src/telemetry/initTelemetry.test.ts +432 -0
- package/src/telemetry/initTelemetry.ts +218 -0
- package/src/telemetry/telemetryManager.test.ts +327 -0
- package/src/telemetry/telemetryManager.ts +199 -0
- package/src/users/reducers.test.ts +6 -1
- package/src/users/reducers.ts +5 -6
- package/src/users/types.ts +4 -4
- package/src/users/usersStore.test.ts +16 -12
- package/src/users/usersStore.ts +5 -1
- package/src/utils/buildQuery.test.ts +14 -0
- package/src/utils/buildQuery.ts +17 -0
- package/src/utils/createFetcherStore.test.ts +7 -5
- package/src/utils/createFetcherStore.ts +8 -5
- package/src/utils/getStagingApiHost.test.ts +21 -0
- package/src/utils/getStagingApiHost.ts +14 -0
- package/src/utils/ids.test.ts +1 -29
- package/src/utils/ids.ts +0 -10
- package/src/utils/includeShape.ts +36 -0
- package/src/utils/isImportError.test.ts +72 -0
- package/src/utils/isImportError.ts +34 -0
- package/src/utils/logger.test.ts +12 -0
- package/src/utils/logger.ts +8 -3
- package/src/utils/object.test.ts +95 -0
- package/src/utils/object.ts +142 -0
- package/src/utils/setCleanupTimeout.ts +37 -0
- package/src/document/processActions.ts +0 -707
- package/src/utils/defineIntent.test.ts +0 -477
- package/src/utils/defineIntent.ts +0 -244
|
@@ -1,37 +1,38 @@
|
|
|
1
|
-
// NOTE: vi.mock REMOVED
|
|
2
|
-
|
|
3
1
|
import {of, Subscription, throwError} from 'rxjs'
|
|
4
|
-
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
|
|
2
|
+
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
|
|
5
3
|
|
|
6
4
|
import {createSanityInstance} from '../store/createSanityInstance'
|
|
7
5
|
import {createStoreState} from '../store/createStoreState'
|
|
8
6
|
import {AuthStateType} from './authStateType'
|
|
9
7
|
import {type AuthState, authStore} from './authStore'
|
|
10
|
-
import {
|
|
11
|
-
getLastRefreshTime,
|
|
12
|
-
getNextRefreshDelay,
|
|
13
|
-
refreshStampedToken,
|
|
14
|
-
setLastRefreshTime,
|
|
15
|
-
} from './refreshStampedToken'
|
|
8
|
+
import {refreshStampedToken} from './refreshStampedToken'
|
|
16
9
|
import {createLoggedInAuthState} from './utils'
|
|
17
10
|
|
|
18
|
-
//
|
|
19
|
-
|
|
11
|
+
// Mock logger to prevent actual logging during tests
|
|
12
|
+
vi.mock('../utils/logger', async (importOriginal) => {
|
|
13
|
+
const original = await importOriginal<typeof import('../utils/logger')>()
|
|
14
|
+
return {
|
|
15
|
+
...original,
|
|
16
|
+
createLogger: vi.fn(() => ({
|
|
17
|
+
info: vi.fn(),
|
|
18
|
+
debug: vi.fn(),
|
|
19
|
+
warn: vi.fn(),
|
|
20
|
+
error: vi.fn(),
|
|
21
|
+
trace: vi.fn(),
|
|
22
|
+
})),
|
|
23
|
+
}
|
|
24
|
+
})
|
|
20
25
|
|
|
21
26
|
describe('refreshStampedToken', () => {
|
|
22
27
|
let mockStorage: Storage
|
|
23
|
-
let originalNavigator: typeof navigator // Restored
|
|
24
28
|
let originalDocument: Document
|
|
25
29
|
let subscriptions: Subscription[]
|
|
26
|
-
// mockLocksRequest removed
|
|
27
30
|
|
|
28
31
|
beforeEach(() => {
|
|
29
32
|
subscriptions = []
|
|
30
33
|
vi.clearAllMocks()
|
|
31
34
|
vi.useFakeTimers()
|
|
32
35
|
|
|
33
|
-
originalNavigator = global.navigator // Restore original navigator setup
|
|
34
|
-
|
|
35
36
|
// Mock document for visibility API
|
|
36
37
|
originalDocument = global.document
|
|
37
38
|
Object.defineProperty(global, 'document', {
|
|
@@ -52,49 +53,10 @@ describe('refreshStampedToken', () => {
|
|
|
52
53
|
key: vi.fn(),
|
|
53
54
|
length: 0,
|
|
54
55
|
}
|
|
55
|
-
// Restore a basic, functional mock for navigator.locks
|
|
56
|
-
// This mock *will* run the callback, including the infinite loop
|
|
57
|
-
// if not handled carefully in tests.
|
|
58
|
-
const mockLocks = {
|
|
59
|
-
request: vi.fn(
|
|
60
|
-
async (
|
|
61
|
-
_name: string,
|
|
62
|
-
_options: LockOptions | LockGrantedCallback,
|
|
63
|
-
callback?: LockGrantedCallback,
|
|
64
|
-
) => {
|
|
65
|
-
const actualCallback = typeof _options === 'function' ? _options : callback
|
|
66
|
-
if (!actualCallback) return false
|
|
67
|
-
const mockLock: Lock = {name: 'mock-lock', mode: 'exclusive'}
|
|
68
|
-
try {
|
|
69
|
-
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
70
|
-
// CAUTION: This executes the callback provided by acquireTokenRefreshLock
|
|
71
|
-
// which contains the infinite loop. Tests need to avoid triggering
|
|
72
|
-
// timers indefinitely if this runs.
|
|
73
|
-
await actualCallback(mockLock)
|
|
74
|
-
// This return is unlikely to be hit if callback loops
|
|
75
|
-
return true
|
|
76
|
-
} catch (error) {
|
|
77
|
-
// eslint-disable-next-line no-console
|
|
78
|
-
console.error('Mock lock request failed:', error)
|
|
79
|
-
return false
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
),
|
|
83
|
-
query: vi.fn(async () => ({held: [], pending: []})),
|
|
84
|
-
}
|
|
85
|
-
Object.defineProperty(global, 'navigator', {
|
|
86
|
-
value: {locks: mockLocks},
|
|
87
|
-
writable: true,
|
|
88
|
-
})
|
|
89
56
|
})
|
|
90
57
|
|
|
91
58
|
afterEach(async () => {
|
|
92
59
|
subscriptions.forEach((sub) => sub.unsubscribe())
|
|
93
|
-
// Restore original navigator
|
|
94
|
-
Object.defineProperty(global, 'navigator', {
|
|
95
|
-
value: originalNavigator,
|
|
96
|
-
writable: true,
|
|
97
|
-
})
|
|
98
60
|
// Restore original document
|
|
99
61
|
Object.defineProperty(global, 'document', {
|
|
100
62
|
value: originalDocument,
|
|
@@ -110,351 +72,229 @@ describe('refreshStampedToken', () => {
|
|
|
110
72
|
vi.useRealTimers()
|
|
111
73
|
})
|
|
112
74
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
})
|
|
123
|
-
const initialState = authStore.getInitialState(instance, null)
|
|
124
|
-
initialState.authState = {
|
|
125
|
-
type: AuthStateType.LOGGED_IN,
|
|
126
|
-
token: 'sk-initial-token-st123',
|
|
127
|
-
currentUser: null,
|
|
128
|
-
}
|
|
129
|
-
initialState.dashboardContext = {mode: 'test'}
|
|
130
|
-
const state = createStoreState(initialState)
|
|
131
|
-
|
|
132
|
-
const subscription = refreshStampedToken({state, instance, key: null})
|
|
133
|
-
subscriptions.push(subscription)
|
|
134
|
-
|
|
135
|
-
await vi.advanceTimersToNextTimerAsync()
|
|
136
|
-
|
|
137
|
-
const finalAuthStateDash = state.get().authState
|
|
138
|
-
expect(finalAuthStateDash.type).toBe(AuthStateType.LOGGED_IN)
|
|
139
|
-
// Ensure token was updated
|
|
140
|
-
if (finalAuthStateDash.type === AuthStateType.LOGGED_IN) {
|
|
141
|
-
expect(finalAuthStateDash.token).toBe('sk-refreshed-token-st123')
|
|
142
|
-
}
|
|
143
|
-
// Verify navigator.locks.request was NOT called
|
|
144
|
-
const locksRequest = navigator.locks.request as ReturnType<typeof vi.fn>
|
|
145
|
-
expect(locksRequest).not.toHaveBeenCalled()
|
|
75
|
+
it('refreshes the token when the interval timer fires', async () => {
|
|
76
|
+
const mockClient = {
|
|
77
|
+
observable: {request: vi.fn(() => of({token: 'sk-refreshed-token-st123'}))},
|
|
78
|
+
}
|
|
79
|
+
const mockClientFactory = vi.fn().mockReturnValue(mockClient)
|
|
80
|
+
const instance = createSanityInstance({
|
|
81
|
+
projectId: 'p',
|
|
82
|
+
dataset: 'd',
|
|
83
|
+
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
|
|
146
84
|
})
|
|
85
|
+
const initialState = authStore.getInitialState(instance, null)
|
|
86
|
+
initialState.authState = {
|
|
87
|
+
type: AuthStateType.LOGGED_IN,
|
|
88
|
+
token: 'sk-initial-token-st123',
|
|
89
|
+
currentUser: null,
|
|
90
|
+
}
|
|
91
|
+
const state = createStoreState(initialState)
|
|
92
|
+
|
|
93
|
+
const subscription = refreshStampedToken({state, instance, key: null})
|
|
94
|
+
subscriptions.push(subscription)
|
|
95
|
+
|
|
96
|
+
await vi.advanceTimersToNextTimerAsync()
|
|
97
|
+
|
|
98
|
+
const finalAuthState = state.get().authState
|
|
99
|
+
expect(finalAuthState.type).toBe(AuthStateType.LOGGED_IN)
|
|
100
|
+
if (finalAuthState.type === AuthStateType.LOGGED_IN) {
|
|
101
|
+
expect(finalAuthState.token).toBe('sk-refreshed-token-st123')
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('writes the refreshed token to storage', async () => {
|
|
106
|
+
const mockClient = {
|
|
107
|
+
observable: {request: vi.fn(() => of({token: 'sk-refreshed-token-st123'}))},
|
|
108
|
+
}
|
|
109
|
+
const mockClientFactory = vi.fn().mockReturnValue(mockClient)
|
|
110
|
+
const instance = createSanityInstance({
|
|
111
|
+
projectId: 'p',
|
|
112
|
+
dataset: 'd',
|
|
113
|
+
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
|
|
114
|
+
})
|
|
115
|
+
const initialState = authStore.getInitialState(instance, null)
|
|
116
|
+
initialState.authState = {
|
|
117
|
+
type: AuthStateType.LOGGED_IN,
|
|
118
|
+
token: 'sk-initial-token-st123',
|
|
119
|
+
currentUser: null,
|
|
120
|
+
}
|
|
121
|
+
const state = createStoreState(initialState)
|
|
122
|
+
|
|
123
|
+
const subscription = refreshStampedToken({state, instance, key: null})
|
|
124
|
+
subscriptions.push(subscription)
|
|
125
|
+
|
|
126
|
+
await vi.advanceTimersToNextTimerAsync()
|
|
127
|
+
|
|
128
|
+
expect(mockClient.observable.request).toHaveBeenCalled()
|
|
129
|
+
expect(mockStorage.setItem).toHaveBeenCalledWith(
|
|
130
|
+
initialState.options.storageKey,
|
|
131
|
+
JSON.stringify({token: 'sk-refreshed-token-st123'}),
|
|
132
|
+
)
|
|
133
|
+
})
|
|
147
134
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
|
|
156
|
-
})
|
|
157
|
-
const initialState = authStore.getInitialState(instance, null)
|
|
158
|
-
initialState.authState = createLoggedInAuthState('sk-initial-token-st123', null)
|
|
159
|
-
initialState.dashboardContext = {mode: 'test'}
|
|
160
|
-
const state = createStoreState(initialState)
|
|
161
|
-
|
|
162
|
-
const subscription = refreshStampedToken({state, instance, key: null})
|
|
163
|
-
subscriptions.push(subscription)
|
|
164
|
-
|
|
165
|
-
const addEventListenerMock = global.document.addEventListener as ReturnType<typeof vi.fn>
|
|
166
|
-
expect(addEventListenerMock).toHaveBeenCalledWith('visibilitychange', expect.any(Function))
|
|
167
|
-
const visibilityHandler = addEventListenerMock.mock.calls[0][1] as () => void
|
|
168
|
-
|
|
169
|
-
Object.defineProperty(global.document, 'visibilityState', {
|
|
170
|
-
value: 'visible',
|
|
171
|
-
writable: true,
|
|
172
|
-
configurable: true,
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
visibilityHandler()
|
|
176
|
-
await vi.advanceTimersByTimeAsync(100)
|
|
177
|
-
|
|
178
|
-
expect(mockClient.observable.request).not.toHaveBeenCalled()
|
|
179
|
-
const finalAuthState = state.get().authState
|
|
180
|
-
if (finalAuthState.type === AuthStateType.LOGGED_IN) {
|
|
181
|
-
expect(finalAuthState.token).toBe('sk-initial-token-st123')
|
|
182
|
-
}
|
|
135
|
+
it('does not refresh on visibility change when lastTokenRefresh is recent', async () => {
|
|
136
|
+
const mockClient = {
|
|
137
|
+
observable: {request: vi.fn(() => of({token: 'sk-refreshed-token-st123'}))},
|
|
138
|
+
}
|
|
139
|
+
const mockClientFactory = vi.fn().mockReturnValue(mockClient)
|
|
140
|
+
const instance = createSanityInstance({
|
|
141
|
+
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
|
|
183
142
|
})
|
|
143
|
+
const initialState = authStore.getInitialState(instance, null)
|
|
144
|
+
initialState.authState = createLoggedInAuthState('sk-initial-token-st123', null)
|
|
145
|
+
const state = createStoreState(initialState)
|
|
146
|
+
|
|
147
|
+
const subscription = refreshStampedToken({state, instance, key: null})
|
|
148
|
+
subscriptions.push(subscription)
|
|
184
149
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
|
|
194
|
-
})
|
|
195
|
-
const initialState = authStore.getInitialState(instance, null)
|
|
196
|
-
const staleTimestamp = Date.now() - REFRESH_INTERVAL - 1000
|
|
197
|
-
initialState.authState = {
|
|
198
|
-
type: AuthStateType.LOGGED_IN,
|
|
199
|
-
token: 'sk-initial-token-st123',
|
|
200
|
-
currentUser: null,
|
|
201
|
-
lastTokenRefresh: staleTimestamp,
|
|
202
|
-
}
|
|
203
|
-
initialState.dashboardContext = {mode: 'test'}
|
|
204
|
-
const state = createStoreState(initialState)
|
|
205
|
-
|
|
206
|
-
const subscription = refreshStampedToken({state, instance, key: null})
|
|
207
|
-
subscriptions.push(subscription)
|
|
208
|
-
|
|
209
|
-
const addEventListenerMock = global.document.addEventListener as ReturnType<typeof vi.fn>
|
|
210
|
-
expect(addEventListenerMock).toHaveBeenCalledWith('visibilitychange', expect.any(Function))
|
|
211
|
-
const visibilityHandler = addEventListenerMock.mock.calls[0][1] as () => void
|
|
212
|
-
|
|
213
|
-
Object.defineProperty(global.document, 'visibilityState', {
|
|
214
|
-
value: 'visible',
|
|
215
|
-
writable: true,
|
|
216
|
-
configurable: true,
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
visibilityHandler()
|
|
220
|
-
await vi.advanceTimersToNextTimerAsync()
|
|
221
|
-
|
|
222
|
-
expect(mockClient.observable.request).toHaveBeenCalled()
|
|
223
|
-
const finalAuthState = state.get().authState
|
|
224
|
-
if (finalAuthState.type === AuthStateType.LOGGED_IN) {
|
|
225
|
-
expect(finalAuthState.token).toBe('sk-refreshed-token-st123')
|
|
226
|
-
expect(finalAuthState.lastTokenRefresh).toBeGreaterThan(staleTimestamp)
|
|
227
|
-
}
|
|
150
|
+
const addEventListenerMock = global.document.addEventListener as ReturnType<typeof vi.fn>
|
|
151
|
+
expect(addEventListenerMock).toHaveBeenCalledWith('visibilitychange', expect.any(Function))
|
|
152
|
+
const visibilityHandler = addEventListenerMock.mock.calls[0][1] as () => void
|
|
153
|
+
|
|
154
|
+
Object.defineProperty(global.document, 'visibilityState', {
|
|
155
|
+
value: 'visible',
|
|
156
|
+
writable: true,
|
|
157
|
+
configurable: true,
|
|
228
158
|
})
|
|
229
159
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const subscription = refreshStampedToken({state, instance, key: null})
|
|
250
|
-
subscriptions.push(subscription)
|
|
251
|
-
|
|
252
|
-
const addEventListenerMock = global.document.addEventListener as ReturnType<typeof vi.fn>
|
|
253
|
-
const visibilityHandler = addEventListenerMock.mock.calls[0][1] as () => void
|
|
254
|
-
|
|
255
|
-
Object.defineProperty(global.document, 'visibilityState', {
|
|
256
|
-
value: 'visible',
|
|
257
|
-
writable: true,
|
|
258
|
-
configurable: true,
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
visibilityHandler()
|
|
262
|
-
await vi.advanceTimersToNextTimerAsync()
|
|
263
|
-
|
|
264
|
-
// Without lastTokenRefresh, shouldRefreshToken returns true — this is the bug
|
|
265
|
-
expect(mockClient.observable.request).toHaveBeenCalled()
|
|
160
|
+
visibilityHandler()
|
|
161
|
+
await vi.advanceTimersByTimeAsync(100)
|
|
162
|
+
|
|
163
|
+
expect(mockClient.observable.request).not.toHaveBeenCalled()
|
|
164
|
+
const finalAuthState = state.get().authState
|
|
165
|
+
if (finalAuthState.type === AuthStateType.LOGGED_IN) {
|
|
166
|
+
expect(finalAuthState.token).toBe('sk-initial-token-st123')
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('refreshes on visibility change when lastTokenRefresh is stale', async () => {
|
|
171
|
+
const REFRESH_INTERVAL = 12 * 60 * 60 * 1000
|
|
172
|
+
const mockClient = {
|
|
173
|
+
observable: {request: vi.fn(() => of({token: 'sk-refreshed-token-st123'}))},
|
|
174
|
+
}
|
|
175
|
+
const mockClientFactory = vi.fn().mockReturnValue(mockClient)
|
|
176
|
+
const instance = createSanityInstance({
|
|
177
|
+
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
|
|
266
178
|
})
|
|
179
|
+
const initialState = authStore.getInitialState(instance, null)
|
|
180
|
+
const staleTimestamp = Date.now() - REFRESH_INTERVAL - 1000
|
|
181
|
+
initialState.authState = {
|
|
182
|
+
type: AuthStateType.LOGGED_IN,
|
|
183
|
+
token: 'sk-initial-token-st123',
|
|
184
|
+
currentUser: null,
|
|
185
|
+
lastTokenRefresh: staleTimestamp,
|
|
186
|
+
}
|
|
187
|
+
const state = createStoreState(initialState)
|
|
267
188
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const mockClient = {
|
|
281
|
-
observable: {request: vi.fn(() => of({token: 'sk-refreshed-token-st123'}))},
|
|
282
|
-
}
|
|
283
|
-
const mockClientFactory = vi.fn().mockReturnValue(mockClient)
|
|
284
|
-
const instance = createSanityInstance({
|
|
285
|
-
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
|
|
286
|
-
})
|
|
287
|
-
const initialState = authStore.getInitialState(instance, null)
|
|
288
|
-
initialState.authState = {
|
|
289
|
-
type: AuthStateType.LOGGED_IN,
|
|
290
|
-
token: 'sk-initial-token-st123',
|
|
291
|
-
currentUser: null,
|
|
292
|
-
}
|
|
293
|
-
initialState.dashboardContext = {mode: 'test'}
|
|
294
|
-
const state = createStoreState(initialState)
|
|
295
|
-
|
|
296
|
-
const subscription = refreshStampedToken({state, instance, key: null})
|
|
297
|
-
subscriptions.push(subscription)
|
|
298
|
-
|
|
299
|
-
await vi.advanceTimersToNextTimerAsync()
|
|
300
|
-
|
|
301
|
-
// Verify that no refresh occurred
|
|
302
|
-
expect(mockClient.observable.request).not.toHaveBeenCalled()
|
|
303
|
-
const finalAuthState = state.get().authState
|
|
304
|
-
if (finalAuthState.type === AuthStateType.LOGGED_IN) {
|
|
305
|
-
expect(finalAuthState.token).toBe('sk-initial-token-st123')
|
|
306
|
-
}
|
|
189
|
+
const subscription = refreshStampedToken({state, instance, key: null})
|
|
190
|
+
subscriptions.push(subscription)
|
|
191
|
+
|
|
192
|
+
const addEventListenerMock = global.document.addEventListener as ReturnType<typeof vi.fn>
|
|
193
|
+
expect(addEventListenerMock).toHaveBeenCalledWith('visibilitychange', expect.any(Function))
|
|
194
|
+
const visibilityHandler = addEventListenerMock.mock.calls[0][1] as () => void
|
|
195
|
+
|
|
196
|
+
Object.defineProperty(global.document, 'visibilityState', {
|
|
197
|
+
value: 'visible',
|
|
198
|
+
writable: true,
|
|
199
|
+
configurable: true,
|
|
307
200
|
})
|
|
201
|
+
|
|
202
|
+
visibilityHandler()
|
|
203
|
+
await vi.advanceTimersToNextTimerAsync()
|
|
204
|
+
|
|
205
|
+
expect(mockClient.observable.request).toHaveBeenCalled()
|
|
206
|
+
const finalAuthState = state.get().authState
|
|
207
|
+
if (finalAuthState.type === AuthStateType.LOGGED_IN) {
|
|
208
|
+
expect(finalAuthState.token).toBe('sk-refreshed-token-st123')
|
|
209
|
+
expect(finalAuthState.lastTokenRefresh).toBeGreaterThan(staleTimestamp)
|
|
210
|
+
}
|
|
308
211
|
})
|
|
309
212
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
|
|
318
|
-
})
|
|
319
|
-
const initialState = authStore.getInitialState(instance, null)
|
|
320
|
-
initialState.authState = {
|
|
321
|
-
type: AuthStateType.LOGGED_IN,
|
|
322
|
-
token: 'sk-initial-token-st123',
|
|
323
|
-
currentUser: null,
|
|
324
|
-
}
|
|
325
|
-
const state = createStoreState(initialState)
|
|
326
|
-
|
|
327
|
-
let subscription: Subscription | undefined
|
|
328
|
-
// We expect this NOT to throw, but accept we can't easily test the lock call or outcome
|
|
329
|
-
expect(() => {
|
|
330
|
-
subscription = refreshStampedToken({state, instance, key: null})
|
|
331
|
-
subscriptions.push(subscription!)
|
|
332
|
-
}).not.toThrow()
|
|
333
|
-
|
|
334
|
-
// DO NOT advance timers or yield here - focus on immediate observable logic
|
|
335
|
-
// We cannot reliably test that failingLocksRequest is called due to async/timer issues,
|
|
336
|
-
// but we *can* test the consequence of it resolving to false.
|
|
337
|
-
|
|
338
|
-
// VERIFY THE OUTCOME:
|
|
339
|
-
// Check client request was NOT made (because filter(hasLock => hasLock) receives false)
|
|
340
|
-
expect(mockClient.observable.request).not.toHaveBeenCalled()
|
|
341
|
-
// Check state remains unchanged
|
|
342
|
-
const finalAuthState = state.get().authState
|
|
343
|
-
expect(finalAuthState.type).toBe(AuthStateType.LOGGED_IN)
|
|
344
|
-
if (finalAuthState.type === AuthStateType.LOGGED_IN) {
|
|
345
|
-
expect(finalAuthState.token).toBe('sk-initial-token-st123')
|
|
346
|
-
}
|
|
213
|
+
it('refreshes on visibility change when lastTokenRefresh is undefined (pre-fix behavior)', async () => {
|
|
214
|
+
const mockClient = {
|
|
215
|
+
observable: {request: vi.fn(() => of({token: 'sk-refreshed-token-st123'}))},
|
|
216
|
+
}
|
|
217
|
+
const mockClientFactory = vi.fn().mockReturnValue(mockClient)
|
|
218
|
+
const instance = createSanityInstance({
|
|
219
|
+
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
|
|
347
220
|
})
|
|
221
|
+
const initialState = authStore.getInitialState(instance, null)
|
|
222
|
+
initialState.authState = {
|
|
223
|
+
type: AuthStateType.LOGGED_IN,
|
|
224
|
+
token: 'sk-initial-token-st123',
|
|
225
|
+
currentUser: null,
|
|
226
|
+
// lastTokenRefresh intentionally omitted to demonstrate the old bug
|
|
227
|
+
}
|
|
228
|
+
const state = createStoreState(initialState)
|
|
229
|
+
|
|
230
|
+
const subscription = refreshStampedToken({state, instance, key: null})
|
|
231
|
+
subscriptions.push(subscription)
|
|
232
|
+
|
|
233
|
+
const addEventListenerMock = global.document.addEventListener as ReturnType<typeof vi.fn>
|
|
234
|
+
const visibilityHandler = addEventListenerMock.mock.calls[0][1] as () => void
|
|
348
235
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
// Use mockResolvedValue: the from(acquireTokenRefreshLock(...)) expects a Promise
|
|
354
|
-
const failingLocksRequest = vi.fn().mockResolvedValue(false)
|
|
355
|
-
Object.defineProperty(global, 'navigator', {
|
|
356
|
-
value: {locks: {...originalLocks, request: failingLocksRequest}},
|
|
357
|
-
writable: true,
|
|
358
|
-
})
|
|
359
|
-
|
|
360
|
-
try {
|
|
361
|
-
const mockClient = {
|
|
362
|
-
observable: {request: vi.fn(() => of({token: 'sk-refreshed-token-st123'}))},
|
|
363
|
-
}
|
|
364
|
-
const mockClientFactory = vi.fn().mockReturnValue(mockClient)
|
|
365
|
-
const instance = createSanityInstance({
|
|
366
|
-
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
|
|
367
|
-
})
|
|
368
|
-
const initialState = authStore.getInitialState(instance, null)
|
|
369
|
-
initialState.authState = {
|
|
370
|
-
type: AuthStateType.LOGGED_IN,
|
|
371
|
-
token: 'sk-initial-token-st123',
|
|
372
|
-
currentUser: null,
|
|
373
|
-
}
|
|
374
|
-
const state = createStoreState(initialState)
|
|
375
|
-
|
|
376
|
-
const subscription = refreshStampedToken({state, instance, key: null})
|
|
377
|
-
subscriptions.push(subscription)
|
|
378
|
-
|
|
379
|
-
// DO NOT advance timers or yield here - focus on immediate observable logic
|
|
380
|
-
// We cannot reliably test that failingLocksRequest is called due to async/timer issues,
|
|
381
|
-
// but we *can* test the consequence of it resolving to false.
|
|
382
|
-
|
|
383
|
-
// VERIFY THE OUTCOME:
|
|
384
|
-
// Check client request was NOT made (because filter(hasLock => hasLock) receives false)
|
|
385
|
-
expect(mockClient.observable.request).not.toHaveBeenCalled()
|
|
386
|
-
// Check state remains unchanged
|
|
387
|
-
const finalAuthState = state.get().authState
|
|
388
|
-
expect(finalAuthState.type).toBe(AuthStateType.LOGGED_IN)
|
|
389
|
-
if (finalAuthState.type === AuthStateType.LOGGED_IN) {
|
|
390
|
-
expect(finalAuthState.token).toBe('sk-initial-token-st123')
|
|
391
|
-
}
|
|
392
|
-
} finally {
|
|
393
|
-
// Restore original navigator.locks
|
|
394
|
-
Object.defineProperty(global, 'navigator', {value: {locks: originalLocks}, writable: true})
|
|
395
|
-
}
|
|
236
|
+
Object.defineProperty(global.document, 'visibilityState', {
|
|
237
|
+
value: 'visible',
|
|
238
|
+
writable: true,
|
|
239
|
+
configurable: true,
|
|
396
240
|
})
|
|
241
|
+
|
|
242
|
+
visibilityHandler()
|
|
243
|
+
await vi.advanceTimersToNextTimerAsync()
|
|
244
|
+
|
|
245
|
+
// Without lastTokenRefresh, shouldRefreshToken returns true — this is the bug
|
|
246
|
+
expect(mockClient.observable.request).toHaveBeenCalled()
|
|
397
247
|
})
|
|
398
248
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
token: 'sk-initial-token-st123',
|
|
420
|
-
currentUser: null,
|
|
421
|
-
}
|
|
422
|
-
const state = createStoreState(initialState)
|
|
423
|
-
|
|
424
|
-
const subscription = refreshStampedToken({state, instance, key: null})
|
|
425
|
-
subscriptions.push(subscription)
|
|
426
|
-
|
|
427
|
-
// Advance timers to allow the async `performRefresh` to execute
|
|
428
|
-
await vi.advanceTimersToNextTimerAsync()
|
|
429
|
-
|
|
430
|
-
// Verify the refresh was performed and state was updated
|
|
431
|
-
expect(mockClient.observable.request).toHaveBeenCalled()
|
|
432
|
-
const finalAuthState = state.get().authState
|
|
433
|
-
expect(finalAuthState.type).toBe(AuthStateType.LOGGED_IN)
|
|
434
|
-
if (finalAuthState.type === AuthStateType.LOGGED_IN) {
|
|
435
|
-
expect(finalAuthState.token).toBe('sk-refreshed-immediately-st123')
|
|
436
|
-
}
|
|
437
|
-
// Verify token was set in storage
|
|
438
|
-
expect(mockStorage.setItem).toHaveBeenCalledWith(
|
|
439
|
-
initialState.options.storageKey,
|
|
440
|
-
JSON.stringify({token: 'sk-refreshed-immediately-st123'}),
|
|
441
|
-
)
|
|
442
|
-
} finally {
|
|
443
|
-
// Restore navigator.locks
|
|
444
|
-
Object.defineProperty(global.navigator, 'locks', {
|
|
445
|
-
value: originalLocks,
|
|
446
|
-
writable: true,
|
|
447
|
-
})
|
|
448
|
-
}
|
|
249
|
+
it('does not refresh when tab is not visible', async () => {
|
|
250
|
+
// Set visibility to hidden
|
|
251
|
+
Object.defineProperty(global, 'document', {
|
|
252
|
+
value: {
|
|
253
|
+
visibilityState: 'hidden',
|
|
254
|
+
addEventListener: vi.fn(),
|
|
255
|
+
removeEventListener: vi.fn(),
|
|
256
|
+
},
|
|
257
|
+
writable: true,
|
|
258
|
+
configurable: true,
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
const mockClient = {
|
|
262
|
+
observable: {request: vi.fn(() => of({token: 'sk-refreshed-token-st123'}))},
|
|
263
|
+
}
|
|
264
|
+
const mockClientFactory = vi.fn().mockReturnValue(mockClient)
|
|
265
|
+
const instance = createSanityInstance({
|
|
266
|
+
projectId: 'p',
|
|
267
|
+
dataset: 'd',
|
|
268
|
+
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
|
|
449
269
|
})
|
|
270
|
+
const initialState = authStore.getInitialState(instance, null)
|
|
271
|
+
initialState.authState = {
|
|
272
|
+
type: AuthStateType.LOGGED_IN,
|
|
273
|
+
token: 'sk-initial-token-st123',
|
|
274
|
+
currentUser: null,
|
|
275
|
+
}
|
|
276
|
+
const state = createStoreState(initialState)
|
|
277
|
+
|
|
278
|
+
const subscription = refreshStampedToken({state, instance, key: null})
|
|
279
|
+
subscriptions.push(subscription)
|
|
280
|
+
|
|
281
|
+
await vi.advanceTimersToNextTimerAsync()
|
|
282
|
+
|
|
283
|
+
// Verify that no refresh occurred
|
|
284
|
+
expect(mockClient.observable.request).not.toHaveBeenCalled()
|
|
285
|
+
const finalAuthState = state.get().authState
|
|
286
|
+
if (finalAuthState.type === AuthStateType.LOGGED_IN) {
|
|
287
|
+
expect(finalAuthState.token).toBe('sk-initial-token-st123')
|
|
288
|
+
}
|
|
450
289
|
})
|
|
451
290
|
|
|
452
|
-
// Restore other tests to their simpler form
|
|
453
291
|
it('sets an error state when token refresh fails', async () => {
|
|
454
292
|
const error = new Error('Refresh failed')
|
|
455
293
|
const mockClient = {observable: {request: vi.fn(() => throwError(() => error))}}
|
|
456
294
|
const mockClientFactory = vi.fn().mockReturnValue(mockClient)
|
|
457
295
|
const instance = createSanityInstance({
|
|
296
|
+
projectId: 'p',
|
|
297
|
+
dataset: 'd',
|
|
458
298
|
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
|
|
459
299
|
})
|
|
460
300
|
const initialState = authStore.getInitialState(instance, null)
|
|
@@ -463,7 +303,6 @@ describe('refreshStampedToken', () => {
|
|
|
463
303
|
token: 'sk-initial-token-st123',
|
|
464
304
|
currentUser: null,
|
|
465
305
|
}
|
|
466
|
-
initialState.dashboardContext = {mode: 'test'}
|
|
467
306
|
const state = createStoreState(initialState)
|
|
468
307
|
|
|
469
308
|
const subscription = refreshStampedToken({state, instance, key: null})
|
|
@@ -482,6 +321,8 @@ describe('refreshStampedToken', () => {
|
|
|
482
321
|
it('does nothing if user is not logged in', async () => {
|
|
483
322
|
const mockClientFactory = vi.fn()
|
|
484
323
|
const instance = createSanityInstance({
|
|
324
|
+
projectId: 'p',
|
|
325
|
+
dataset: 'd',
|
|
485
326
|
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
|
|
486
327
|
})
|
|
487
328
|
const initialState = authStore.getInitialState(instance, null)
|
|
@@ -497,8 +338,6 @@ describe('refreshStampedToken', () => {
|
|
|
497
338
|
await vi.advanceTimersByTimeAsync(0)
|
|
498
339
|
|
|
499
340
|
expect(mockClientFactory).not.toHaveBeenCalled()
|
|
500
|
-
const locksRequest = navigator.locks.request as ReturnType<typeof vi.fn>
|
|
501
|
-
expect(locksRequest).not.toHaveBeenCalled()
|
|
502
341
|
expect(state.get().authState.type).toBe(AuthStateType.LOGGED_OUT)
|
|
503
342
|
})
|
|
504
343
|
|
|
@@ -506,6 +345,8 @@ describe('refreshStampedToken', () => {
|
|
|
506
345
|
const mockClient = {observable: {request: vi.fn()}}
|
|
507
346
|
const mockClientFactory = vi.fn().mockReturnValue(mockClient)
|
|
508
347
|
const instance = createSanityInstance({
|
|
348
|
+
projectId: 'p',
|
|
349
|
+
dataset: 'd',
|
|
509
350
|
auth: {clientFactory: mockClientFactory, storageArea: mockStorage},
|
|
510
351
|
})
|
|
511
352
|
const initialState = authStore.getInitialState(instance, null)
|
|
@@ -522,8 +363,6 @@ describe('refreshStampedToken', () => {
|
|
|
522
363
|
await vi.advanceTimersByTimeAsync(0)
|
|
523
364
|
|
|
524
365
|
expect(mockClient.observable.request).not.toHaveBeenCalled()
|
|
525
|
-
const locksRequest = navigator.locks.request as ReturnType<typeof vi.fn>
|
|
526
|
-
expect(locksRequest).not.toHaveBeenCalled()
|
|
527
366
|
// Add type guard before accessing token property
|
|
528
367
|
const finalAuthState = state.get().authState
|
|
529
368
|
expect(finalAuthState.type).toBe(AuthStateType.LOGGED_IN)
|
|
@@ -532,92 +371,3 @@ describe('refreshStampedToken', () => {
|
|
|
532
371
|
}
|
|
533
372
|
})
|
|
534
373
|
})
|
|
535
|
-
|
|
536
|
-
describe('time-based refresh helpers', () => {
|
|
537
|
-
let mockStorage: Storage
|
|
538
|
-
const storageKey = 'my-test-key'
|
|
539
|
-
|
|
540
|
-
beforeEach(() => {
|
|
541
|
-
mockStorage = {
|
|
542
|
-
getItem: vi.fn(),
|
|
543
|
-
setItem: vi.fn(),
|
|
544
|
-
removeItem: vi.fn(),
|
|
545
|
-
clear: vi.fn(),
|
|
546
|
-
key: vi.fn(),
|
|
547
|
-
length: 0,
|
|
548
|
-
}
|
|
549
|
-
vi.useFakeTimers()
|
|
550
|
-
})
|
|
551
|
-
|
|
552
|
-
afterEach(() => {
|
|
553
|
-
vi.useRealTimers()
|
|
554
|
-
})
|
|
555
|
-
|
|
556
|
-
describe('getLastRefreshTime', () => {
|
|
557
|
-
it('returns 0 if storage is undefined', () => {
|
|
558
|
-
expect(getLastRefreshTime(undefined, storageKey)).toBe(0)
|
|
559
|
-
})
|
|
560
|
-
|
|
561
|
-
it('returns 0 if item is not in storage', () => {
|
|
562
|
-
vi.spyOn(mockStorage, 'getItem').mockReturnValue(null)
|
|
563
|
-
expect(getLastRefreshTime(mockStorage, storageKey)).toBe(0)
|
|
564
|
-
expect(mockStorage.getItem).toHaveBeenCalledWith(`${storageKey}_last_refresh`)
|
|
565
|
-
})
|
|
566
|
-
|
|
567
|
-
it('returns the parsed timestamp from storage', () => {
|
|
568
|
-
const now = Date.now()
|
|
569
|
-
vi.spyOn(mockStorage, 'getItem').mockReturnValue(now.toString())
|
|
570
|
-
expect(getLastRefreshTime(mockStorage, storageKey)).toBe(now)
|
|
571
|
-
})
|
|
572
|
-
|
|
573
|
-
it('returns 0 if stored data is malformed', () => {
|
|
574
|
-
vi.spyOn(mockStorage, 'getItem').mockReturnValue('not a number')
|
|
575
|
-
expect(getLastRefreshTime(mockStorage, storageKey)).toBe(0)
|
|
576
|
-
})
|
|
577
|
-
|
|
578
|
-
it('returns 0 on storage access error', () => {
|
|
579
|
-
vi.spyOn(mockStorage, 'getItem').mockImplementation(() => {
|
|
580
|
-
throw new Error('Storage access failed')
|
|
581
|
-
})
|
|
582
|
-
expect(getLastRefreshTime(mockStorage, storageKey)).toBe(0)
|
|
583
|
-
})
|
|
584
|
-
})
|
|
585
|
-
|
|
586
|
-
describe('setLastRefreshTime', () => {
|
|
587
|
-
it('sets the current timestamp in storage', () => {
|
|
588
|
-
const now = Date.now()
|
|
589
|
-
setLastRefreshTime(mockStorage, storageKey)
|
|
590
|
-
expect(mockStorage.setItem).toHaveBeenCalledWith(`${storageKey}_last_refresh`, now.toString())
|
|
591
|
-
})
|
|
592
|
-
|
|
593
|
-
it('does not throw on storage access error', () => {
|
|
594
|
-
vi.spyOn(mockStorage, 'setItem').mockImplementation(() => {
|
|
595
|
-
throw new Error('Storage access failed')
|
|
596
|
-
})
|
|
597
|
-
expect(() => setLastRefreshTime(mockStorage, storageKey)).not.toThrow()
|
|
598
|
-
})
|
|
599
|
-
})
|
|
600
|
-
|
|
601
|
-
describe('getNextRefreshDelay', () => {
|
|
602
|
-
const REFRESH_INTERVAL = 12 * 60 * 60 * 1000
|
|
603
|
-
|
|
604
|
-
it('returns 0 if last refresh time is not available', () => {
|
|
605
|
-
vi.spyOn(mockStorage, 'getItem').mockReturnValue(null)
|
|
606
|
-
expect(getNextRefreshDelay(mockStorage, storageKey)).toBe(0)
|
|
607
|
-
})
|
|
608
|
-
|
|
609
|
-
it('returns the remaining time until the next refresh', () => {
|
|
610
|
-
const lastRefresh = Date.now() - 10000 // 10 seconds ago
|
|
611
|
-
vi.spyOn(mockStorage, 'getItem').mockReturnValue(lastRefresh.toString())
|
|
612
|
-
|
|
613
|
-
const delay = getNextRefreshDelay(mockStorage, storageKey)
|
|
614
|
-
expect(delay).toBeCloseTo(REFRESH_INTERVAL - 10000, -2)
|
|
615
|
-
})
|
|
616
|
-
|
|
617
|
-
it('returns 0 if the refresh interval has passed', () => {
|
|
618
|
-
const lastRefresh = Date.now() - REFRESH_INTERVAL - 5000 // 5 seconds past due
|
|
619
|
-
vi.spyOn(mockStorage, 'getItem').mockReturnValue(lastRefresh.toString())
|
|
620
|
-
expect(getNextRefreshDelay(mockStorage, storageKey)).toBe(0)
|
|
621
|
-
})
|
|
622
|
-
})
|
|
623
|
-
})
|