@morscherlab/mint-sdk 1.0.1 → 1.0.3
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morscherlab/mint-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "MINT Platform SDK — Vue 3 components, composables, and types for plugin development. MINT = Mass-spec INtegrated Toolkit.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
2
|
-
import { mount } from '@vue/test-utils'
|
|
2
|
+
import { flushPromises, mount, type VueWrapper } from '@vue/test-utils'
|
|
3
3
|
import { computed, ref } from 'vue'
|
|
4
4
|
import { createPinia } from 'pinia'
|
|
5
5
|
import AppTopBar from '../../components/AppTopBar.vue'
|
|
@@ -64,6 +64,17 @@ function createWrapper(props = {}, slots = {}) {
|
|
|
64
64
|
})
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
async function waitForAsyncComponents(wrapper: VueWrapper) {
|
|
68
|
+
await flushPromises()
|
|
69
|
+
await wrapper.vm.$nextTick()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function openSettingsModal(wrapper: VueWrapper) {
|
|
73
|
+
await wrapper.get('.mint-topbar__settings-btn').trigger('click')
|
|
74
|
+
await waitForAsyncComponents(wrapper)
|
|
75
|
+
return wrapper.findComponent(SettingsModal)
|
|
76
|
+
}
|
|
77
|
+
|
|
67
78
|
describe('AppTopBar', () => {
|
|
68
79
|
beforeEach(() => {
|
|
69
80
|
vi.clearAllMocks()
|
|
@@ -147,12 +158,15 @@ describe('AppTopBar', () => {
|
|
|
147
158
|
expect(settingsBtn.exists()).toBe(true)
|
|
148
159
|
})
|
|
149
160
|
|
|
150
|
-
it('should render SettingsModal
|
|
161
|
+
it('should render SettingsModal after settings is opened', async () => {
|
|
151
162
|
const wrapper = createWrapper({
|
|
152
163
|
title: 'Test App',
|
|
153
164
|
showSettings: true,
|
|
154
165
|
})
|
|
155
|
-
expect(wrapper.findComponent(SettingsModal).exists()).toBe(
|
|
166
|
+
expect(wrapper.findComponent(SettingsModal).exists()).toBe(false)
|
|
167
|
+
|
|
168
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
169
|
+
expect(settingsModal.exists()).toBe(true)
|
|
156
170
|
})
|
|
157
171
|
|
|
158
172
|
it('should have aria-label on settings button', () => {
|
|
@@ -183,26 +197,22 @@ describe('AppTopBar', () => {
|
|
|
183
197
|
showSettings: true,
|
|
184
198
|
})
|
|
185
199
|
|
|
186
|
-
const
|
|
187
|
-
await settingsBtn.trigger('click')
|
|
188
|
-
|
|
189
|
-
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
200
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
190
201
|
expect(settingsModal.props('modelValue')).toBe(true)
|
|
191
202
|
})
|
|
192
203
|
|
|
193
|
-
it('should
|
|
204
|
+
it('should not mount SettingsModal while closed', () => {
|
|
194
205
|
const wrapper = createWrapper({
|
|
195
206
|
title: 'Test App',
|
|
196
207
|
showSettings: true,
|
|
197
208
|
})
|
|
198
209
|
|
|
199
|
-
|
|
200
|
-
expect(settingsModal.props('modelValue')).toBe(false)
|
|
210
|
+
expect(wrapper.findComponent(SettingsModal).exists()).toBe(false)
|
|
201
211
|
})
|
|
202
212
|
})
|
|
203
213
|
|
|
204
214
|
describe('settingsConfig prop', () => {
|
|
205
|
-
it('should pass title to SettingsModal', () => {
|
|
215
|
+
it('should pass title to SettingsModal', async () => {
|
|
206
216
|
const settingsConfig: TopBarSettingsConfig = {
|
|
207
217
|
title: 'Custom Settings',
|
|
208
218
|
}
|
|
@@ -213,11 +223,11 @@ describe('AppTopBar', () => {
|
|
|
213
223
|
settingsConfig,
|
|
214
224
|
})
|
|
215
225
|
|
|
216
|
-
const settingsModal = wrapper
|
|
226
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
217
227
|
expect(settingsModal.props('title')).toBe('Custom Settings')
|
|
218
228
|
})
|
|
219
229
|
|
|
220
|
-
it('should pass tabs to SettingsModal', () => {
|
|
230
|
+
it('should pass tabs to SettingsModal', async () => {
|
|
221
231
|
const settingsConfig: TopBarSettingsConfig = {
|
|
222
232
|
tabs: [
|
|
223
233
|
{ id: 'general', label: 'General' },
|
|
@@ -231,11 +241,11 @@ describe('AppTopBar', () => {
|
|
|
231
241
|
settingsConfig,
|
|
232
242
|
})
|
|
233
243
|
|
|
234
|
-
const settingsModal = wrapper
|
|
244
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
235
245
|
expect(settingsModal.props('tabs')).toEqual(settingsConfig.tabs)
|
|
236
246
|
})
|
|
237
247
|
|
|
238
|
-
it('should normalize shorthand settings tabs for SettingsModal', () => {
|
|
248
|
+
it('should normalize shorthand settings tabs for SettingsModal', async () => {
|
|
239
249
|
const wrapper = createWrapper({
|
|
240
250
|
title: 'Test App',
|
|
241
251
|
showSettings: true,
|
|
@@ -245,7 +255,7 @@ describe('AppTopBar', () => {
|
|
|
245
255
|
},
|
|
246
256
|
})
|
|
247
257
|
|
|
248
|
-
const settingsModal = wrapper
|
|
258
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
249
259
|
expect(settingsModal.props('tabs')).toEqual([
|
|
250
260
|
{ id: 'General', label: 'General' },
|
|
251
261
|
{ id: 'Advanced', label: 'Advanced' },
|
|
@@ -287,18 +297,20 @@ describe('AppTopBar', () => {
|
|
|
287
297
|
settingsConfig,
|
|
288
298
|
})
|
|
289
299
|
|
|
300
|
+
await openSettingsModal(wrapper)
|
|
290
301
|
await wrapper.setProps({
|
|
291
302
|
settingsConfig: {
|
|
292
303
|
...settingsConfig,
|
|
293
304
|
values: { threshold: 0.75, method: 'logistic', externalContext: 'experiment-2' },
|
|
294
305
|
},
|
|
295
306
|
})
|
|
307
|
+
await waitForAsyncComponents(wrapper)
|
|
296
308
|
|
|
297
309
|
expect((wrapper.find('input').element as HTMLInputElement).value).toBe('0.75')
|
|
298
310
|
expect((wrapper.find('select').element as HTMLSelectElement).value).toBe('logistic')
|
|
299
311
|
})
|
|
300
312
|
|
|
301
|
-
it('should render compact controls in SettingsModal', () => {
|
|
313
|
+
it('should render compact controls in SettingsModal', async () => {
|
|
302
314
|
const controls = defineControls({
|
|
303
315
|
threshold: {
|
|
304
316
|
label: 'Threshold',
|
|
@@ -318,13 +330,13 @@ describe('AppTopBar', () => {
|
|
|
318
330
|
settingsConfig,
|
|
319
331
|
})
|
|
320
332
|
|
|
321
|
-
const settingsModal = wrapper
|
|
333
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
322
334
|
expect(settingsModal.props('controls')).toStrictEqual(controls)
|
|
323
335
|
expect(wrapper.text()).toContain('Threshold')
|
|
324
336
|
expect((wrapper.find('input').element as HTMLInputElement).value).toBe('0.25')
|
|
325
337
|
})
|
|
326
338
|
|
|
327
|
-
it('should pass control model to SettingsModal', () => {
|
|
339
|
+
it('should pass control model to SettingsModal', async () => {
|
|
328
340
|
const model = defineControlModel({
|
|
329
341
|
controls: {
|
|
330
342
|
threshold: {
|
|
@@ -346,13 +358,13 @@ describe('AppTopBar', () => {
|
|
|
346
358
|
settingsConfig,
|
|
347
359
|
})
|
|
348
360
|
|
|
349
|
-
const settingsModal = wrapper
|
|
361
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
350
362
|
expect(settingsModal.props('model')).toStrictEqual(model)
|
|
351
363
|
expect(wrapper.text()).toContain('Threshold')
|
|
352
364
|
expect((wrapper.find('input').element as HTMLInputElement).value).toBe('0.25')
|
|
353
365
|
})
|
|
354
366
|
|
|
355
|
-
it('should pass showAppearance to SettingsModal', () => {
|
|
367
|
+
it('should pass showAppearance to SettingsModal', async () => {
|
|
356
368
|
const settingsConfig: TopBarSettingsConfig = {
|
|
357
369
|
showAppearance: false,
|
|
358
370
|
}
|
|
@@ -363,11 +375,11 @@ describe('AppTopBar', () => {
|
|
|
363
375
|
settingsConfig,
|
|
364
376
|
})
|
|
365
377
|
|
|
366
|
-
const settingsModal = wrapper
|
|
378
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
367
379
|
expect(settingsModal.props('showAppearance')).toBe(false)
|
|
368
380
|
})
|
|
369
381
|
|
|
370
|
-
it('should pass settings userType to SettingsModal', () => {
|
|
382
|
+
it('should pass settings userType to SettingsModal', async () => {
|
|
371
383
|
const settingsConfig: TopBarSettingsConfig = {
|
|
372
384
|
userType: 'admin',
|
|
373
385
|
}
|
|
@@ -378,22 +390,22 @@ describe('AppTopBar', () => {
|
|
|
378
390
|
settingsConfig,
|
|
379
391
|
})
|
|
380
392
|
|
|
381
|
-
const settingsModal = wrapper
|
|
393
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
382
394
|
expect(settingsModal.props('userType')).toBe('admin')
|
|
383
395
|
})
|
|
384
396
|
|
|
385
|
-
it('should default showAppearance to true when not specified', () => {
|
|
397
|
+
it('should default showAppearance to true when not specified', async () => {
|
|
386
398
|
const wrapper = createWrapper({
|
|
387
399
|
title: 'Test App',
|
|
388
400
|
showSettings: true,
|
|
389
401
|
settingsConfig: {},
|
|
390
402
|
})
|
|
391
403
|
|
|
392
|
-
const settingsModal = wrapper
|
|
404
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
393
405
|
expect(settingsModal.props('showAppearance')).toBe(true)
|
|
394
406
|
})
|
|
395
407
|
|
|
396
|
-
it('should pass size to SettingsModal', () => {
|
|
408
|
+
it('should pass size to SettingsModal', async () => {
|
|
397
409
|
const settingsConfig: TopBarSettingsConfig = {
|
|
398
410
|
size: 'xl',
|
|
399
411
|
}
|
|
@@ -404,24 +416,24 @@ describe('AppTopBar', () => {
|
|
|
404
416
|
settingsConfig,
|
|
405
417
|
})
|
|
406
418
|
|
|
407
|
-
const settingsModal = wrapper
|
|
419
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
408
420
|
expect(settingsModal.props('size')).toBe('xl')
|
|
409
421
|
})
|
|
410
422
|
|
|
411
|
-
it('should use default size when not specified', () => {
|
|
423
|
+
it('should use default size when not specified', async () => {
|
|
412
424
|
const wrapper = createWrapper({
|
|
413
425
|
title: 'Test App',
|
|
414
426
|
showSettings: true,
|
|
415
427
|
})
|
|
416
428
|
|
|
417
|
-
const settingsModal = wrapper
|
|
429
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
418
430
|
// SettingsModal defaults to 'lg' size
|
|
419
431
|
expect(settingsModal.props('size')).toBe('lg')
|
|
420
432
|
})
|
|
421
433
|
})
|
|
422
434
|
|
|
423
435
|
describe('settings tab slot forwarding', () => {
|
|
424
|
-
it('should forward settings-tab slots to SettingsModal', () => {
|
|
436
|
+
it('should forward settings-tab slots to SettingsModal', async () => {
|
|
425
437
|
const settingsConfig: TopBarSettingsConfig = {
|
|
426
438
|
tabs: [{ id: 'general', label: 'General' }],
|
|
427
439
|
}
|
|
@@ -434,11 +446,11 @@ describe('AppTopBar', () => {
|
|
|
434
446
|
'settings-tab-general': '<div class="custom-tab-content">Custom General Tab</div>',
|
|
435
447
|
})
|
|
436
448
|
|
|
437
|
-
const settingsModal = wrapper
|
|
449
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
438
450
|
expect(settingsModal.vm.$slots['tab-general']).toBeDefined()
|
|
439
451
|
})
|
|
440
452
|
|
|
441
|
-
it('should forward shorthand settings-tab slots to SettingsModal', () => {
|
|
453
|
+
it('should forward shorthand settings-tab slots to SettingsModal', async () => {
|
|
442
454
|
const wrapper = createWrapper({
|
|
443
455
|
title: 'Test App',
|
|
444
456
|
showSettings: true,
|
|
@@ -450,11 +462,11 @@ describe('AppTopBar', () => {
|
|
|
450
462
|
'settings-tab-General': '<div>General Content</div>',
|
|
451
463
|
})
|
|
452
464
|
|
|
453
|
-
const settingsModal = wrapper
|
|
465
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
454
466
|
expect(settingsModal.vm.$slots['tab-General']).toBeDefined()
|
|
455
467
|
})
|
|
456
468
|
|
|
457
|
-
it('should forward multiple settings-tab slots', () => {
|
|
469
|
+
it('should forward multiple settings-tab slots', async () => {
|
|
458
470
|
const settingsConfig: TopBarSettingsConfig = {
|
|
459
471
|
tabs: [
|
|
460
472
|
{ id: 'general', label: 'General' },
|
|
@@ -471,12 +483,12 @@ describe('AppTopBar', () => {
|
|
|
471
483
|
'settings-tab-advanced': '<div>Advanced Content</div>',
|
|
472
484
|
})
|
|
473
485
|
|
|
474
|
-
const settingsModal = wrapper
|
|
486
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
475
487
|
expect(settingsModal.vm.$slots['tab-general']).toBeDefined()
|
|
476
488
|
expect(settingsModal.vm.$slots['tab-advanced']).toBeDefined()
|
|
477
489
|
})
|
|
478
490
|
|
|
479
|
-
it('should forward settings-appearance slot', () => {
|
|
491
|
+
it('should forward settings-appearance slot', async () => {
|
|
480
492
|
const wrapper = createWrapper({
|
|
481
493
|
title: 'Test App',
|
|
482
494
|
showSettings: true,
|
|
@@ -484,7 +496,7 @@ describe('AppTopBar', () => {
|
|
|
484
496
|
'settings-appearance': '<div class="custom-appearance">Custom Appearance</div>',
|
|
485
497
|
})
|
|
486
498
|
|
|
487
|
-
const settingsModal = wrapper
|
|
499
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
488
500
|
expect(settingsModal.vm.$slots.appearance).toBeDefined()
|
|
489
501
|
})
|
|
490
502
|
})
|
|
@@ -551,7 +563,7 @@ describe('AppTopBar', () => {
|
|
|
551
563
|
})
|
|
552
564
|
|
|
553
565
|
describe('combined features', () => {
|
|
554
|
-
it('passes injected app experiment bindings to the popover and selector modal', () => {
|
|
566
|
+
it('passes injected app experiment bindings to the popover and selector modal', async () => {
|
|
555
567
|
vi.mocked(usePlatformContext).mockReturnValueOnce({
|
|
556
568
|
isIntegrated: computed(() => true),
|
|
557
569
|
context: ref({ isIntegrated: true, theme: 'system' }),
|
|
@@ -587,7 +599,7 @@ describe('AppTopBar', () => {
|
|
|
587
599
|
saveSuccessMessage: 'Saved',
|
|
588
600
|
})),
|
|
589
601
|
selectorModal: computed(() => ({
|
|
590
|
-
modelValue:
|
|
602
|
+
modelValue: true,
|
|
591
603
|
currentExperimentId: 12,
|
|
592
604
|
})),
|
|
593
605
|
openModal: vi.fn(),
|
|
@@ -614,6 +626,7 @@ describe('AppTopBar', () => {
|
|
|
614
626
|
},
|
|
615
627
|
})
|
|
616
628
|
|
|
629
|
+
await waitForAsyncComponents(wrapper)
|
|
617
630
|
expect(wrapper.findComponent(ExperimentPopover).props()).toMatchObject({
|
|
618
631
|
experimentName: 'Dose response',
|
|
619
632
|
experimentCode: 'EXP-012',
|
|
@@ -626,7 +639,7 @@ describe('AppTopBar', () => {
|
|
|
626
639
|
saveSuccessMessage: 'Saved',
|
|
627
640
|
})
|
|
628
641
|
expect(wrapper.findComponent(ExperimentSelectorModal).props()).toMatchObject({
|
|
629
|
-
modelValue:
|
|
642
|
+
modelValue: true,
|
|
630
643
|
currentExperimentId: 12,
|
|
631
644
|
})
|
|
632
645
|
})
|
|
@@ -822,14 +835,14 @@ describe('AppTopBar', () => {
|
|
|
822
835
|
})
|
|
823
836
|
|
|
824
837
|
describe('edge cases', () => {
|
|
825
|
-
it('should handle empty settingsConfig object', () => {
|
|
838
|
+
it('should handle empty settingsConfig object', async () => {
|
|
826
839
|
const wrapper = createWrapper({
|
|
827
840
|
title: 'Test App',
|
|
828
841
|
showSettings: true,
|
|
829
842
|
settingsConfig: {},
|
|
830
843
|
})
|
|
831
844
|
|
|
832
|
-
const settingsModal = wrapper
|
|
845
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
833
846
|
// SettingsModal has default values for these props
|
|
834
847
|
expect(settingsModal.props('title')).toBe('Settings')
|
|
835
848
|
expect(settingsModal.props('tabs')).toEqual([])
|
|
@@ -837,7 +850,7 @@ describe('AppTopBar', () => {
|
|
|
837
850
|
expect(settingsModal.props('size')).toBe('lg')
|
|
838
851
|
})
|
|
839
852
|
|
|
840
|
-
it('should handle settingsConfig with only some fields', () => {
|
|
853
|
+
it('should handle settingsConfig with only some fields', async () => {
|
|
841
854
|
const settingsConfig: TopBarSettingsConfig = {
|
|
842
855
|
title: 'Partial Config',
|
|
843
856
|
}
|
|
@@ -848,7 +861,7 @@ describe('AppTopBar', () => {
|
|
|
848
861
|
settingsConfig,
|
|
849
862
|
})
|
|
850
863
|
|
|
851
|
-
const settingsModal = wrapper
|
|
864
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
852
865
|
expect(settingsModal.props('title')).toBe('Partial Config')
|
|
853
866
|
// SettingsModal defaults tabs to empty array
|
|
854
867
|
expect(settingsModal.props('tabs')).toEqual([])
|
|
@@ -880,7 +893,7 @@ describe('AppTopBar', () => {
|
|
|
880
893
|
})
|
|
881
894
|
|
|
882
895
|
describe('type safety', () => {
|
|
883
|
-
it('should accept valid settingsConfig with all fields', () => {
|
|
896
|
+
it('should accept valid settingsConfig with all fields', async () => {
|
|
884
897
|
const settingsConfig: TopBarSettingsConfig = {
|
|
885
898
|
title: 'Complete Settings',
|
|
886
899
|
tabs: [
|
|
@@ -897,22 +910,22 @@ describe('AppTopBar', () => {
|
|
|
897
910
|
settingsConfig,
|
|
898
911
|
})
|
|
899
912
|
|
|
900
|
-
expect(wrapper
|
|
913
|
+
expect((await openSettingsModal(wrapper)).exists()).toBe(true)
|
|
901
914
|
})
|
|
902
915
|
|
|
903
|
-
it('should accept all valid size options', () => {
|
|
916
|
+
it('should accept all valid size options', async () => {
|
|
904
917
|
const sizes: Array<'md' | 'lg' | 'xl'> = ['md', 'lg', 'xl']
|
|
905
918
|
|
|
906
|
-
|
|
919
|
+
for (const size of sizes) {
|
|
907
920
|
const wrapper = createWrapper({
|
|
908
921
|
title: 'Test App',
|
|
909
922
|
showSettings: true,
|
|
910
923
|
settingsConfig: { size },
|
|
911
924
|
})
|
|
912
925
|
|
|
913
|
-
const settingsModal = wrapper
|
|
926
|
+
const settingsModal = await openSettingsModal(wrapper)
|
|
914
927
|
expect(settingsModal.props('size')).toBe(size)
|
|
915
|
-
}
|
|
928
|
+
}
|
|
916
929
|
})
|
|
917
930
|
})
|
|
918
931
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mount } from '@vue/test-utils'
|
|
1
|
+
import { flushPromises, mount } from '@vue/test-utils'
|
|
2
2
|
import { createPinia } from 'pinia'
|
|
3
3
|
import { computed, h, nextTick, ref } from 'vue'
|
|
4
4
|
import { describe, expect, it, vi } from 'vitest'
|
|
@@ -316,6 +316,9 @@ describe('PluginWorkspaceView', () => {
|
|
|
316
316
|
global: globalOptions,
|
|
317
317
|
})
|
|
318
318
|
|
|
319
|
+
await flushPromises()
|
|
320
|
+
await nextTick()
|
|
321
|
+
|
|
319
322
|
expect(wrapper.findComponent(ExperimentPopover).props()).toMatchObject({
|
|
320
323
|
experimentName: 'Dose response',
|
|
321
324
|
experimentCode: 'EXP-012',
|
|
@@ -327,6 +330,10 @@ describe('PluginWorkspaceView', () => {
|
|
|
327
330
|
saveLoading: false,
|
|
328
331
|
})
|
|
329
332
|
|
|
333
|
+
wrapper.findComponent(ExperimentPopover).vm.$emit('select')
|
|
334
|
+
await flushPromises()
|
|
335
|
+
await nextTick()
|
|
336
|
+
|
|
330
337
|
wrapper.findComponent(ExperimentSelectorModal).vm.$emit('select', {
|
|
331
338
|
id: 13,
|
|
332
339
|
name: 'Selected experiment',
|