import { mount } from '@vue/test-utils' import { h, nextTick } from 'vue' import { describe, expect, it, vi } from 'vitest' import AppSidebar from '../../components/AppSidebar.vue' import BioTemplatePresetWorkspaceView from '../../components/BioTemplatePresetWorkspaceView.vue' import { useBioTemplatePresetWorkspace } from '../../composables/useBioTemplatePresetWorkspace' vi.mock('../../composables/useApi', () => ({ useApi: () => ({ get: vi.fn(), put: vi.fn(), delete: vi.fn(), }), })) describe('BioTemplatePresetWorkspaceView', () => { it('renders preset controls and template previews from one workspace object', () => { const workspace = useBioTemplatePresetWorkspace('wellplate-screen') const wrapper = mount(BioTemplatePresetWorkspaceView, { props: { workspace }, global: { stubs: { DataFrame: true, DoseCalculator: true, PlateEditor: true, SampleSelector: true, WellPlate: true, }, }, }) expect(wrapper.find('.mint-bio-template-preset-workspace').exists()).toBe(true) expect(wrapper.text()).toContain('Well-plate screen preset saves') expect(wrapper.find('.mint-sidebar').exists()).toBe(true) expect(wrapper.find('.mint-bio-template-renderer').exists()).toBe(true) expect(wrapper.text()).toContain('plate-map') expect(wrapper.text()).toContain('dose-response') }) it('can create the preset workspace internally from a preset id', () => { const wrapper = mount(BioTemplatePresetWorkspaceView, { props: { preset: 'wellplate-screen', workspaceOptions: { initialValues: { sampleNames: ['Vehicle', 'Drug'], unit: 'nM', }, }, }, global: { stubs: { DataFrame: true, DoseCalculator: true, PlateEditor: true, SampleSelector: true, WellPlate: true, }, }, }) expect(wrapper.text()).toContain('Well-plate screen preset saves') expect(wrapper.findComponent(AppSidebar).props('modelValue')).toMatchObject({ sampleNames: ['Vehicle', 'Drug'], unit: 'nM', }) expect(wrapper.text()).toContain('plate-map') expect(wrapper.text()).toContain('dose-response') }) it('updates the internally generated workspace when the preset changes after mount', async () => { const wrapper = mount(BioTemplatePresetWorkspaceView, { props: { preset: 'wellplate-screen', }, global: { stubs: { DataFrame: true, DoseCalculator: true, PlateEditor: true, SampleSelector: true, WellPlate: true, }, }, }) expect(wrapper.text()).toContain('Well-plate screen preset saves') expect(wrapper.findComponent(AppSidebar).props('activeView')).toBe('design') await wrapper.setProps({ preset: 'lcms-batch' }) expect(wrapper.text()).toContain('LC-MS batch preset saves') expect(wrapper.findComponent(AppSidebar).props('activeView')).toBe('run') expect(wrapper.text()).toContain('instrument-run') expect(wrapper.text()).toContain('assay-matrix') }) it('applies workspace options that are loaded after mount', async () => { const wrapper = mount(BioTemplatePresetWorkspaceView, { props: { preset: 'wellplate-screen', }, global: { stubs: { DataFrame: true, DoseCalculator: true, PlateEditor: true, SampleSelector: true, WellPlate: true, }, }, }) expect(wrapper.findComponent(AppSidebar).props('modelValue')).toMatchObject({ sampleNames: ['Control', 'Treatment'], }) await wrapper.setProps({ workspaceOptions: { initialValues: { sampleNames: ['Vehicle', 'Drug'], unit: 'nM', }, }, }) expect(wrapper.findComponent(AppSidebar).props('modelValue')).toMatchObject({ sampleNames: ['Vehicle', 'Drug'], unit: 'nM', }) }) it('supports v-model control values for internally generated workspaces', async () => { const wrapper = mount(BioTemplatePresetWorkspaceView, { props: { preset: 'wellplate-screen', modelValue: { sampleNames: ['Vehicle', 'Drug'], unit: 'nM', }, }, global: { stubs: { DataFrame: true, DoseCalculator: true, PlateEditor: true, SampleSelector: true, WellPlate: true, }, }, }) expect(wrapper.findComponent(AppSidebar).props('modelValue')).toMatchObject({ sampleNames: ['Vehicle', 'Drug'], unit: 'nM', }) wrapper.findComponent(AppSidebar).vm.$emit('update:modelValue', { sampleNames: ['Vehicle', 'Updated'], unit: 'nM', }) await nextTick() expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual([ expect.objectContaining({ sampleNames: ['Vehicle', 'Updated'], unit: 'nM', }), ]) expect(wrapper.emitted('update:values')?.at(-1)).toEqual([ expect.objectContaining({ sampleNames: ['Vehicle', 'Updated'], unit: 'nM', }), ]) await wrapper.setProps({ modelValue: { sampleNames: ['External'], unit: 'pM', }, }) await nextTick() expect(wrapper.findComponent(AppSidebar).props('modelValue')).toMatchObject({ sampleNames: ['External'], unit: 'pM', }) }) it('exposes generated component props to the default slot', () => { const wrapper = mount(BioTemplatePresetWorkspaceView, { props: { preset: 'wellplate-screen', }, slots: { default: ({ bindings, collection, componentBindingsById, componentPropsById, getComponentProps }) => h( 'pre', { class: 'component-props' }, JSON.stringify({ preset: collection.metadata?.preset, doseMode: componentPropsById['dose-response:DoseCalculator'].mode, bindingMode: componentBindingsById['dose-response:DoseCalculator'].props.mode, bindingDoseMode: bindings.componentPropsById.value['dose-response:DoseCalculator'].mode, nestedBindingMode: bindings.componentBindingsById.value['dose-response:DoseCalculator'].props.mode, hasPlateWells: Boolean(componentPropsById['plate-map:WellPlate'].wells), doseModeFromGetter: getComponentProps('DoseCalculator')?.mode, bindingDoseModeFromGetter: bindings.getComponentProps('DoseCalculator')?.mode, }), ), }, global: { stubs: { DataFrame: true, DoseCalculator: true, PlateEditor: true, SampleSelector: true, WellPlate: true, }, }, }) expect(JSON.parse(wrapper.find('.component-props').text())).toEqual({ preset: 'wellplate-screen', doseMode: 'serial', bindingMode: 'serial', bindingDoseMode: 'serial', nestedBindingMode: 'serial', hasPlateWells: true, doseModeFromGetter: 'serial', bindingDoseModeFromGetter: 'serial', }) expect(wrapper.find('.mint-bio-template-renderer').exists()).toBe(false) }) it('updates workspace control values through the embedded sidebar', async () => { const workspace = useBioTemplatePresetWorkspace('wellplate-screen') const wrapper = mount(BioTemplatePresetWorkspaceView, { props: { workspace }, global: { stubs: { DataFrame: true, DoseCalculator: true, PlateEditor: true, SampleSelector: true, WellPlate: true, }, }, }) wrapper.findComponent(AppSidebar).vm.$emit('update:modelValue', { ...workspace.controlValues.value, sampleNames: ['Vehicle', 'Drug'], }) await nextTick() expect(workspace.controlValues.value.sampleNames).toEqual(['Vehicle', 'Drug']) expect(workspace.sidebar.modelValue.sampleNames).toEqual(['Vehicle', 'Drug']) }) it('switches generated sidebar views for mixed-view presets', async () => { const workspace = useBioTemplatePresetWorkspace('lcms-batch') const wrapper = mount(BioTemplatePresetWorkspaceView, { props: { workspace }, global: { stubs: { DataFrame: true, DoseCalculator: true, PlateEditor: true, SampleSelector: true, WellPlate: true, }, }, }) expect(wrapper.find('.mint-pill-nav').exists()).toBe(true) expect(wrapper.findComponent(AppSidebar).props('activeView')).toBe('run') await wrapper.findAll('.mint-pill-nav__item')[1].trigger('click') await nextTick() expect(workspace.activeControlView.value).toBe('analysis') expect(wrapper.findComponent(AppSidebar).props('activeView')).toBe('analysis') }) it('renders LC-MS batch timeline and calendar previews from one preset prop', () => { const wrapper = mount(BioTemplatePresetWorkspaceView, { props: { preset: 'lcms-batch', }, global: { stubs: { DataFrame: true, ExperimentTimeline: true, ScheduleCalendar: true, SampleSelector: true, }, }, }) expect(wrapper.text()).toContain('LC-MS batch preset saves') expect(wrapper.find('[data-component-binding-id="instrument-run:ExperimentTimeline"]').exists()).toBe(true) expect(wrapper.find('[data-component-binding-id="instrument-run:ScheduleCalendar"]').exists()).toBe(true) expect(wrapper.find('[data-template-component="ExperimentTimeline"]').exists()).toBe(true) expect(wrapper.find('[data-template-component="ScheduleCalendar"]').exists()).toBe(true) }) })