import { mount } from '@vue/test-utils' import { h, nextTick } from 'vue' import { describe, expect, it, vi } from 'vitest' import BioTemplateExperimentWorkspaceView from '../../components/BioTemplateExperimentWorkspaceView.vue' import BioTemplatePackWorkspaceView from '../../components/BioTemplatePackWorkspaceView.vue' import { useBioTemplatePackWorkspace } from '../../composables/useBioTemplatePackWorkspace' const mockGet = vi.fn() const mockPut = vi.fn() const mockDelete = vi.fn() vi.mock('../../composables/useApi', () => ({ useApi: () => ({ get: mockGet, put: mockPut, delete: mockDelete, }), })) const globalOptions = { stubs: { DataFrame: true, DoseCalculator: true, ExperimentTimeline: true, PlateEditor: true, SampleSelector: true, ScheduleCalendar: true, WellPlate: true, }, } describe('BioTemplatePackWorkspaceView', () => { it('creates a saveable pack workspace from one pack id', () => { const wrapper = mount(BioTemplatePackWorkspaceView, { props: { pack: 'cell culture', }, global: globalOptions, }) expect(wrapper.text()).toContain('Cell culture screen saves the full template collection') expect(wrapper.text()).toContain('sample-sheet') expect(wrapper.text()).toContain('dose-response') expect(wrapper.find('.mint-bio-template-renderer').exists()).toBe(true) }) it('forwards load, reset, and save actions to the pack workspace', async () => { const workspace = useBioTemplatePackWorkspace('cell-culture-screen') const loadCurrent = vi.fn() const resetToDefaultCollection = vi.fn() const saveCurrent = vi.fn() workspace.loadCurrent = loadCurrent workspace.resetToDefaultCollection = resetToDefaultCollection workspace.saveCurrent = saveCurrent const wrapper = mount(BioTemplatePackWorkspaceView, { props: { workspace }, global: globalOptions, }) const child = wrapper.findComponent(BioTemplateExperimentWorkspaceView) const actions = child.props('actions') as { load: () => unknown reset: () => unknown save: () => unknown } actions.load() actions.reset() actions.save() await nextTick() expect(loadCurrent).toHaveBeenCalledTimes(1) expect(resetToDefaultCollection).toHaveBeenCalledTimes(1) expect(saveCurrent).toHaveBeenCalledTimes(1) }) it('uses external status and actions for plugin-backed persistence', async () => { const workspace = useBioTemplatePackWorkspace('cell-culture-screen') const load = vi.fn() const reset = vi.fn() const save = vi.fn() const lastLoadedAt = new Date('2026-01-02T03:04:05Z') const wrapper = mount(BioTemplatePackWorkspaceView, { props: { workspace, status: { loading: false, error: 'Plugin request failed.', hasExperiment: true, currentExperimentId: 42, lastLoadedAt, lastSavedAt: null, }, actions: { load, reset, save }, }, global: globalOptions, }) const child = wrapper.findComponent(BioTemplateExperimentWorkspaceView) expect(child.props('status')).toMatchObject({ loading: false, error: 'Plugin request failed.', hasExperiment: true, currentExperimentId: 42, lastLoadedAt, lastSavedAt: null, }) const actions = child.props('actions') as { load: () => unknown reset: () => unknown save: () => unknown } actions.load() actions.reset() actions.save() await nextTick() expect(load).toHaveBeenCalledTimes(1) expect(reset).toHaveBeenCalledTimes(1) expect(save).toHaveBeenCalledTimes(1) }) it('exposes pack workspace and generated component props to the default slot', () => { const wrapper = mount(BioTemplatePackWorkspaceView, { props: { pack: 'cell-culture-screen', }, slots: { default: ({ bindings, pack, componentBindingsById, componentPropsById, form, getComponentProps, pillNav, sidebar, topBarSettings, }) => h( 'pre', { class: 'component-props' }, JSON.stringify({ pack: pack.name, activeView: sidebar.activeView, doseMode: componentPropsById['dose-response:DoseCalculator'].mode, bindingMode: componentBindingsById['dose-response:DoseCalculator'].props.mode, bindingDoseMode: bindings.componentPropsById['dose-response:DoseCalculator'].mode, nestedBindingMode: bindings.componentBindingsById['dose-response:DoseCalculator'].props.mode, hasPlateWells: Boolean(componentPropsById['plate-map:WellPlate'].wells), doseModeFromGetter: getComponentProps('DoseCalculator')?.mode, bindingDoseModeFromGetter: bindings.getComponentProps('DoseCalculator')?.mode, formModelMatchesSidebar: form.modelValue === sidebar.values, navItems: pillNav.items.map((item: { id: string }) => item.id), settingsValuesMatch: topBarSettings.settingsConfig.values === sidebar.values, }), ), }, global: globalOptions, }) expect(JSON.parse(wrapper.find('.component-props').text())).toEqual({ pack: 'cell-culture-screen', activeView: 'design', doseMode: 'serial', bindingMode: 'serial', bindingDoseMode: 'serial', nestedBindingMode: 'serial', hasPlateWells: true, doseModeFromGetter: 'serial', bindingDoseModeFromGetter: 'serial', formModelMatchesSidebar: true, navItems: ['design'], settingsValuesMatch: true, }) expect(wrapper.find('.mint-bio-template-renderer').exists()).toBe(false) }) it('renders metabolism pack run queues as timeline and calendar previews', () => { const wrapper = mount(BioTemplatePackWorkspaceView, { props: { pack: 'metabolism', }, global: globalOptions, }) expect(wrapper.text()).toContain('Omics assay saves the full template collection') 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) }) })