@morscherlab/mint-sdk 1.3.0-beta.1 → 1.3.0-beta.10
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/README.md +23 -140
- package/dist/{ResourcePickerListInternal-c3IT8PIu.js → ResourcePickerListInternal-CFAw4vTZ.js} +26 -19
- package/dist/ResourcePickerListInternal-CFAw4vTZ.js.map +1 -0
- package/dist/components/FilePicker.vue.d.ts +4 -1
- package/dist/components/SettingsModal.schema.d.ts +1 -1
- package/dist/components/SettingsModal.vue.d.ts +2 -0
- package/dist/components/WellPlate.colors.d.ts +1 -5
- package/dist/components/WellPlate.rendering.d.ts +5 -2
- package/dist/components/index.d.ts +32 -17
- package/dist/components/index.js +3 -3
- package/dist/components/internal/FilePickerListInternal.vue.d.ts +2 -0
- package/dist/components/internal/ResourcePickerListInternal.vue.d.ts +9 -3
- package/dist/{components-CvY105kJ.js → components-GDDvof8D.js} +414 -368
- package/dist/components-GDDvof8D.js.map +1 -0
- package/dist/composables/createFilePickerAdapter.d.ts +5 -1
- package/dist/composables/filePicker/usePickerNavigation.d.ts +3 -2
- package/dist/composables/index.d.ts +0 -3
- package/dist/composables/index.js +4 -4
- package/dist/composables/useAutoGroup.d.ts +1 -1
- package/dist/composables/useBioTemplateComponents.d.ts +15 -3
- package/dist/composables/useBioTemplateControls.d.ts +5 -1
- package/dist/composables/useBioTemplatePackWorkspace.d.ts +2 -0
- package/dist/composables/useBioTemplatePresetWorkspace.d.ts +5 -1
- package/dist/composables/useBioTemplateWorkspace.d.ts +5 -1
- package/dist/composables/useExperimentSave.d.ts +5 -1
- package/dist/composables/useFilePicker.d.ts +4 -1
- package/dist/composables/useGroupAssignment.d.ts +5 -1
- package/dist/composables/usePlatformContext.d.ts +3 -0
- package/dist/composables/useTemplateCollection.d.ts +5 -1
- package/dist/composables-D6uqp0UF.js +591 -0
- package/dist/composables-D6uqp0UF.js.map +1 -0
- package/dist/index.js +24 -24
- package/dist/index.js.map +1 -1
- package/dist/install.js +1 -1
- package/dist/lcms.d.ts +11 -11
- package/dist/styles.css +170 -200
- package/dist/templates/index.js +1 -1
- package/dist/{templates-Bj9hZ7iV.js → templates-BU1pet28.js} +31 -44
- package/dist/templates-BU1pet28.js.map +1 -0
- package/dist/types/auth.d.ts +0 -27
- package/dist/types/components.d.ts +8 -0
- package/dist/types/fileBrowserTypes.d.ts +13 -0
- package/dist/types/filePicker.d.ts +8 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/platform.d.ts +2 -0
- package/dist/utils/rack.d.ts +16 -16
- package/dist/{validation-B4Y8_eEe.js → validation-Dmp9Q5W0.js} +50 -12
- package/dist/validation-Dmp9Q5W0.js.map +1 -0
- package/package.json +1 -6
- package/src/__tests__/components/ActionMenu.test.ts +10 -0
- package/src/__tests__/components/AppTopBar.test.ts +24 -8
- package/src/__tests__/components/EmptyState.test.ts +15 -0
- package/src/__tests__/components/ExperimentSelectorModal.test.ts +12 -5
- package/src/__tests__/components/FilePicker.test.ts +65 -1
- package/src/__tests__/components/FileUploader.test.ts +6 -9
- package/src/__tests__/components/ResourcePickerListInternal.test.ts +23 -4
- package/src/__tests__/components/SettingsModal.test.ts +22 -0
- package/src/__tests__/components/WellPlate.decoration.test.ts +4 -28
- package/src/__tests__/components/WellPlate.rendering.test.ts +20 -33
- package/src/__tests__/components/WellPlate.sampleTypeIndicator.test.ts +7 -7
- package/src/__tests__/composables/createFilePickerAdapter.test.ts +92 -1
- package/src/__tests__/composables/usePickerNavigation.test.ts +61 -1
- package/src/__tests__/composables/usePlatformFilePickerAdapter.test.ts +18 -6
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +2 -2
- package/src/__tests__/styles/controlRefreshRules.test.ts +21 -8
- package/src/__tests__/styles/menusShellRefresh.test.ts +20 -5
- package/src/__tests__/templates/templatesDeprecation.test.ts +58 -11
- package/src/components/ActionMenu.vue +10 -2
- package/src/components/AppTopBar.vue +6 -4
- package/src/components/EmptyState.vue +3 -1
- package/src/components/ExperimentSelectorModal.vue +4 -1
- package/src/components/FilePicker.vue +42 -0
- package/src/components/FileUploader.vue +30 -46
- package/src/components/SettingsModal.schema.ts +4 -2
- package/src/components/SettingsModal.vue +4 -2
- package/src/components/WellPlate.colors.ts +6 -10
- package/src/components/WellPlate.legend.ts +4 -4
- package/src/components/WellPlate.rendering.ts +9 -8
- package/src/components/WellPlate.vue +5 -22
- package/src/components/index.ts +32 -17
- package/src/components/internal/FilePickerListInternal.vue +11 -2
- package/src/components/internal/ResourcePickerListInternal.vue +21 -27
- package/src/composables/autoGroup/index.ts +1 -1
- package/src/composables/createFilePickerAdapter.ts +82 -22
- package/src/composables/filePicker/usePickerNavigation.ts +29 -2
- package/src/composables/index.ts +0 -7
- package/src/composables/useAutoGroup.ts +1 -1
- package/src/composables/useBioTemplateComponents.ts +15 -3
- package/src/composables/useBioTemplateControls.ts +5 -1
- package/src/composables/useBioTemplatePackWorkspace.ts +2 -0
- package/src/composables/useBioTemplatePresetWorkspace.ts +5 -1
- package/src/composables/useBioTemplateWorkspace.ts +5 -1
- package/src/composables/useExperimentSave.ts +5 -1
- package/src/composables/useFilePicker.ts +8 -1
- package/src/composables/useGroupAssignment.ts +5 -1
- package/src/composables/usePlatformFilePickerAdapter.ts +26 -4
- package/src/composables/useTemplateCollection.ts +5 -1
- package/src/index.ts +1 -1
- package/src/lcms.ts +11 -11
- package/src/styles/components/action-menu.css +10 -0
- package/src/styles/components/app-page-selector.css +3 -3
- package/src/styles/components/app-pill-nav.css +1 -1
- package/src/styles/components/app-top-bar.css +18 -38
- package/src/styles/components/empty-state.css +3 -2
- package/src/styles/components/experiment-selector-modal.css +3 -9
- package/src/styles/components/file-picker.css +23 -1
- package/src/styles/components/file-uploader.css +22 -55
- package/src/styles/components/resource-picker-list.css +70 -36
- package/src/styles/components/well-plate.css +18 -55
- package/src/types/auth.ts +0 -37
- package/src/types/components.ts +8 -0
- package/src/types/fileBrowserTypes.ts +15 -0
- package/src/types/filePicker.ts +8 -1
- package/src/types/index.ts +2 -5
- package/src/types/platform.ts +2 -0
- package/src/utils/rack.ts +16 -16
- package/dist/ResourcePickerListInternal-c3IT8PIu.js.map +0 -1
- package/dist/__tests__/composables/useAuth.test.d.ts +0 -1
- package/dist/__tests__/composables/usePasskey.test.d.ts +0 -1
- package/dist/components-CvY105kJ.js.map +0 -1
- package/dist/composables/useAuth.d.ts +0 -47
- package/dist/composables/usePasskey.d.ts +0 -10
- package/dist/composables/useWellPlateEditor.d.ts +0 -33
- package/dist/composables-DL4Vca7L.js +0 -1193
- package/dist/composables-DL4Vca7L.js.map +0 -1
- package/dist/plotly.min-D6Ge6qgX.js +0 -168582
- package/dist/plotly.min-D6Ge6qgX.js.map +0 -1
- package/dist/templates-Bj9hZ7iV.js.map +0 -1
- package/dist/validation-B4Y8_eEe.js.map +0 -1
- package/src/__tests__/composables/useAuth.test.ts +0 -290
- package/src/__tests__/composables/usePasskey.test.ts +0 -38
- package/src/composables/useAuth.ts +0 -470
- package/src/composables/usePasskey.ts +0 -216
- package/src/composables/useWellPlateEditor.ts +0 -415
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
2
|
-
import { createPinia, setActivePinia } from 'pinia'
|
|
3
|
-
import axios from 'axios'
|
|
4
|
-
|
|
5
|
-
vi.mock('axios')
|
|
6
|
-
const mockedAxios = vi.mocked(axios, true)
|
|
7
|
-
|
|
8
|
-
const unmountedCallbacks: Array<() => void> = []
|
|
9
|
-
|
|
10
|
-
// Mock vue lifecycle hooks since we're not in a component
|
|
11
|
-
vi.mock('vue', async () => {
|
|
12
|
-
const actual = await vi.importActual<typeof import('vue')>('vue')
|
|
13
|
-
return {
|
|
14
|
-
...actual,
|
|
15
|
-
getCurrentInstance: vi.fn(() => ({})),
|
|
16
|
-
onMounted: vi.fn((cb: () => void) => cb()),
|
|
17
|
-
onUnmounted: vi.fn((cb: () => void) => {
|
|
18
|
-
unmountedCallbacks.push(cb)
|
|
19
|
-
}),
|
|
20
|
-
}
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
import { useAuth, _resetAuthStateForTesting } from '../../composables/useAuth'
|
|
24
|
-
import { useAuthStore } from '../../stores/auth'
|
|
25
|
-
import { useSettingsStore } from '../../stores/settings'
|
|
26
|
-
|
|
27
|
-
describe('useAuth', () => {
|
|
28
|
-
beforeEach(() => {
|
|
29
|
-
setActivePinia(createPinia())
|
|
30
|
-
vi.clearAllMocks()
|
|
31
|
-
vi.useFakeTimers()
|
|
32
|
-
unmountedCallbacks.length = 0
|
|
33
|
-
_resetAuthStateForTesting()
|
|
34
|
-
|
|
35
|
-
// Mock settings store
|
|
36
|
-
const settingsStore = useSettingsStore()
|
|
37
|
-
settingsStore.getApiBaseUrl = vi.fn(() => 'http://localhost:8000')
|
|
38
|
-
|
|
39
|
-
// Mock localStorage
|
|
40
|
-
const storage: Record<string, string> = {}
|
|
41
|
-
vi.stubGlobal('localStorage', {
|
|
42
|
-
getItem: vi.fn((key: string) => storage[key] ?? null),
|
|
43
|
-
setItem: vi.fn((key: string, val: string) => { storage[key] = val }),
|
|
44
|
-
removeItem: vi.fn((key: string) => { delete storage[key] }),
|
|
45
|
-
})
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
afterEach(() => {
|
|
49
|
-
vi.useRealTimers()
|
|
50
|
-
vi.restoreAllMocks()
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('loads the PostgreSQL-only public auth contract', async () => {
|
|
54
|
-
mockedAxios.get.mockResolvedValueOnce({
|
|
55
|
-
data: {
|
|
56
|
-
auth_required: true,
|
|
57
|
-
passkey_enabled: true,
|
|
58
|
-
passkey_registered: false,
|
|
59
|
-
registration_enabled: true,
|
|
60
|
-
database_mode: 'postgresql',
|
|
61
|
-
access: { experiment_visibility_mode: 'restricted' },
|
|
62
|
-
},
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
const config = await useAuth().fetchAuthConfig()
|
|
66
|
-
const expected = {
|
|
67
|
-
authRequired: true,
|
|
68
|
-
passkeyEnabled: true,
|
|
69
|
-
passkeyRegistered: false,
|
|
70
|
-
registrationEnabled: true,
|
|
71
|
-
databaseMode: 'postgresql',
|
|
72
|
-
experimentVisibilityMode: 'restricted',
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
expect([config, useAuthStore().authConfig]).toEqual([expected, expected])
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
describe('login', () => {
|
|
79
|
-
it('should set token on successful login', async () => {
|
|
80
|
-
mockedAxios.post.mockResolvedValueOnce({
|
|
81
|
-
data: { access_token: 'test-token', expires_in: 3600, token_type: 'bearer' },
|
|
82
|
-
})
|
|
83
|
-
mockedAxios.get.mockResolvedValueOnce({
|
|
84
|
-
data: { id: 1, username: 'test', shortname: null, email: null, role: 'user', is_active: true },
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
const { login } = useAuth()
|
|
88
|
-
const result = await login('test', 'password')
|
|
89
|
-
|
|
90
|
-
expect(result).toBe(true)
|
|
91
|
-
const authStore = useAuthStore()
|
|
92
|
-
expect(authStore.token).toBe('test-token')
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
it('should return false on failed login', async () => {
|
|
96
|
-
mockedAxios.post.mockRejectedValueOnce({
|
|
97
|
-
isAxiosError: true,
|
|
98
|
-
response: { data: { detail: 'Invalid credentials' }, status: 401 },
|
|
99
|
-
})
|
|
100
|
-
// @ts-expect-error -- vitest mock assignment
|
|
101
|
-
mockedAxios.isAxiosError = vi.fn(() => true)
|
|
102
|
-
|
|
103
|
-
const { login } = useAuth()
|
|
104
|
-
const result = await login('test', 'wrong')
|
|
105
|
-
|
|
106
|
-
expect(result).toBe(false)
|
|
107
|
-
})
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
describe('logout', () => {
|
|
111
|
-
it('should clear token on logout', async () => {
|
|
112
|
-
const authStore = useAuthStore()
|
|
113
|
-
authStore.setToken('test-token', 3600)
|
|
114
|
-
|
|
115
|
-
const { logout } = useAuth()
|
|
116
|
-
logout()
|
|
117
|
-
|
|
118
|
-
expect(authStore.token).toBeNull()
|
|
119
|
-
})
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
describe('storage fallback', () => {
|
|
123
|
-
it('keeps auth usable when localStorage operations throw', () => {
|
|
124
|
-
vi.stubGlobal('localStorage', {
|
|
125
|
-
getItem: vi.fn(() => { throw new Error('blocked') }),
|
|
126
|
-
setItem: vi.fn(() => { throw new Error('blocked') }),
|
|
127
|
-
removeItem: vi.fn(() => { throw new Error('blocked') }),
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
const authStore = useAuthStore()
|
|
131
|
-
|
|
132
|
-
expect(() => authStore.initialize()).not.toThrow()
|
|
133
|
-
|
|
134
|
-
authStore.setToken('memory-token', 3600)
|
|
135
|
-
expect(authStore.token).toBe('memory-token')
|
|
136
|
-
|
|
137
|
-
authStore.clearToken()
|
|
138
|
-
expect(authStore.token).toBeNull()
|
|
139
|
-
})
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
describe('refreshToken - race condition', () => {
|
|
143
|
-
it('should deduplicate concurrent refresh calls', async () => {
|
|
144
|
-
const authStore = useAuthStore()
|
|
145
|
-
authStore.setToken('old-token', 3600)
|
|
146
|
-
|
|
147
|
-
let resolveRefresh: (value: unknown) => void
|
|
148
|
-
const refreshPromise = new Promise((resolve) => {
|
|
149
|
-
resolveRefresh = resolve
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
mockedAxios.post.mockImplementation(() => refreshPromise as Promise<unknown>)
|
|
153
|
-
|
|
154
|
-
const { refreshToken } = useAuth()
|
|
155
|
-
|
|
156
|
-
// Fire two concurrent refreshes
|
|
157
|
-
const p1 = refreshToken()
|
|
158
|
-
const p2 = refreshToken()
|
|
159
|
-
|
|
160
|
-
// Both should return the same promise
|
|
161
|
-
resolveRefresh!({
|
|
162
|
-
data: { access_token: 'new-token', expires_in: 3600, token_type: 'bearer' },
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
const [r1, r2] = await Promise.all([p1, p2])
|
|
166
|
-
expect(r1).toBe(true)
|
|
167
|
-
expect(r2).toBe(true)
|
|
168
|
-
|
|
169
|
-
// axios.post should have been called only once
|
|
170
|
-
expect(mockedAxios.post).toHaveBeenCalledTimes(1)
|
|
171
|
-
})
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
describe('refreshToken - lifecycle behavior', () => {
|
|
175
|
-
it('keeps scheduled refresh when one of multiple consumers unmounts', async () => {
|
|
176
|
-
mockedAxios.post.mockImplementation((url: string) => {
|
|
177
|
-
if (url.endsWith('/auth/login')) {
|
|
178
|
-
return Promise.resolve({
|
|
179
|
-
data: { access_token: 'test-token', expires_in: 301, token_type: 'bearer' },
|
|
180
|
-
})
|
|
181
|
-
}
|
|
182
|
-
if (url.endsWith('/auth/refresh')) {
|
|
183
|
-
return Promise.resolve({
|
|
184
|
-
data: { access_token: 'refreshed-token', expires_in: 3600, token_type: 'bearer' },
|
|
185
|
-
})
|
|
186
|
-
}
|
|
187
|
-
throw new Error(`Unexpected POST ${url}`)
|
|
188
|
-
})
|
|
189
|
-
mockedAxios.get.mockResolvedValue({
|
|
190
|
-
data: { id: 1, username: 'test', shortname: null, email: null, role: 'user', is_active: true },
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
const authA = useAuth()
|
|
194
|
-
useAuth()
|
|
195
|
-
|
|
196
|
-
await authA.login('test', 'password')
|
|
197
|
-
|
|
198
|
-
expect(unmountedCallbacks).toHaveLength(2)
|
|
199
|
-
unmountedCallbacks[0]!()
|
|
200
|
-
|
|
201
|
-
vi.advanceTimersByTime(2_000)
|
|
202
|
-
await Promise.resolve()
|
|
203
|
-
|
|
204
|
-
const refreshCalls = mockedAxios.post.mock.calls.filter(
|
|
205
|
-
([url]) => typeof url === 'string' && url.endsWith('/auth/refresh'),
|
|
206
|
-
)
|
|
207
|
-
expect(refreshCalls).toHaveLength(1)
|
|
208
|
-
|
|
209
|
-
unmountedCallbacks[1]!()
|
|
210
|
-
})
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
describe('verifyToken', () => {
|
|
214
|
-
it('should return false when no token', async () => {
|
|
215
|
-
const { verifyToken } = useAuth()
|
|
216
|
-
const result = await verifyToken()
|
|
217
|
-
expect(result).toBe(false)
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
it('should return true on valid token', async () => {
|
|
221
|
-
const authStore = useAuthStore()
|
|
222
|
-
authStore.setToken('valid-token', 3600)
|
|
223
|
-
|
|
224
|
-
mockedAxios.get.mockResolvedValueOnce({
|
|
225
|
-
data: { valid: true, username: 'testuser' },
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
const { verifyToken } = useAuth()
|
|
229
|
-
const result = await verifyToken()
|
|
230
|
-
expect(result).toBe(true)
|
|
231
|
-
})
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
describe('updateProfile', () => {
|
|
235
|
-
it('should swap in the replacement token when a password change returns one', async () => {
|
|
236
|
-
const authStore = useAuthStore()
|
|
237
|
-
authStore.setToken('old-token', 3600)
|
|
238
|
-
mockedAxios.put.mockResolvedValueOnce({
|
|
239
|
-
data: {
|
|
240
|
-
id: 1, username: 'test', shortname: null, email: null, role: 'user', is_active: true,
|
|
241
|
-
access_token: 'fresh-token', expires_in: 7200,
|
|
242
|
-
},
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
await useAuth().updateProfile({ currentPassword: 'old', newPassword: 'new-password' })
|
|
246
|
-
|
|
247
|
-
expect(authStore.token).toBe('fresh-token')
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
async function changePasswordDuringRefresh(settle: (reject: (e: unknown) => void, resolve: (v: unknown) => void) => void) {
|
|
251
|
-
const authStore = useAuthStore()
|
|
252
|
-
authStore.setToken('old-token', 3600)
|
|
253
|
-
let resolveRefresh!: (v: unknown) => void
|
|
254
|
-
let rejectRefresh!: (e: unknown) => void
|
|
255
|
-
mockedAxios.post.mockReturnValueOnce(new Promise((resolve, reject) => {
|
|
256
|
-
resolveRefresh = resolve
|
|
257
|
-
rejectRefresh = reject
|
|
258
|
-
}))
|
|
259
|
-
mockedAxios.put.mockResolvedValueOnce({
|
|
260
|
-
data: {
|
|
261
|
-
id: 1, username: 'test', shortname: null, email: null, role: 'user', is_active: true,
|
|
262
|
-
access_token: 'fresh-token', expires_in: 7200,
|
|
263
|
-
},
|
|
264
|
-
})
|
|
265
|
-
const auth = useAuth()
|
|
266
|
-
const refreshing = auth.refreshToken()
|
|
267
|
-
await auth.updateProfile({ currentPassword: 'old', newPassword: 'new-password' })
|
|
268
|
-
settle(rejectRefresh, resolveRefresh)
|
|
269
|
-
await refreshing
|
|
270
|
-
return authStore.token
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
it('should keep the fresh token when an old-token refresh fails with 401 afterwards', async () => {
|
|
274
|
-
mockedAxios.isAxiosError.mockReturnValue(true)
|
|
275
|
-
|
|
276
|
-
const token = await changePasswordDuringRefresh((reject) => reject({ response: { status: 401 } }))
|
|
277
|
-
|
|
278
|
-
expect(token).toBe('fresh-token')
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
it('should keep the fresh token when an old-token refresh succeeds afterwards', async () => {
|
|
282
|
-
const token = await changePasswordDuringRefresh((_reject, resolve) =>
|
|
283
|
-
resolve({ data: { access_token: 'revoked-token', expires_in: 3600, token_type: 'bearer' } }),
|
|
284
|
-
)
|
|
285
|
-
|
|
286
|
-
expect(token).toBe('fresh-token')
|
|
287
|
-
})
|
|
288
|
-
})
|
|
289
|
-
})
|
|
290
|
-
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
-
import { createPinia, setActivePinia } from 'pinia'
|
|
3
|
-
import axios from 'axios'
|
|
4
|
-
import { startAuthentication, startRegistration } from '@simplewebauthn/browser'
|
|
5
|
-
import { usePasskey } from '../../composables/usePasskey'
|
|
6
|
-
|
|
7
|
-
vi.mock('axios')
|
|
8
|
-
vi.mock('@simplewebauthn/browser', () => ({
|
|
9
|
-
browserSupportsWebAuthn: vi.fn(() => true),
|
|
10
|
-
startAuthentication: vi.fn(),
|
|
11
|
-
startRegistration: vi.fn(),
|
|
12
|
-
}))
|
|
13
|
-
|
|
14
|
-
const options = { challenge: 'challenge' }
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
setActivePinia(createPinia())
|
|
18
|
-
vi.mocked(axios.get).mockResolvedValueOnce({ data: { options: JSON.stringify(options) } })
|
|
19
|
-
vi.mocked(axios.post).mockResolvedValueOnce({ data: { access_token: 'token', expires_in: 3600 } })
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
afterEach(() => {
|
|
23
|
-
vi.resetAllMocks()
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
describe('usePasskey', () => {
|
|
27
|
-
it('should pass server options as optionsJSON when logging in', async () => {
|
|
28
|
-
await usePasskey().loginWithPasskey()
|
|
29
|
-
|
|
30
|
-
expect(startAuthentication).toHaveBeenCalledWith({ optionsJSON: options })
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('should pass server options as optionsJSON when registering', async () => {
|
|
34
|
-
await usePasskey().registerPasskey('Laptop')
|
|
35
|
-
|
|
36
|
-
expect(startRegistration).toHaveBeenCalledWith({ optionsJSON: options })
|
|
37
|
-
})
|
|
38
|
-
})
|