import { mount } from '@vue/test-utils' import { describe, expect, it } from 'vitest' import { nextTick } from 'vue' import BaseButton from '../../components/BaseButton.vue' import BaseInput from '../../components/BaseInput.vue' import BaseTextarea from '../../components/BaseTextarea.vue' import ProtocolStepEditor from '../../components/ProtocolStepEditor.vue' import SearchableSelect from '../../components/SearchableSelect.vue' import type { StepTemplate } from '../../composables/useProtocolTemplates' const templates: StepTemplate[] = [ { id: 'inc', name: 'Incubation', type: 'incubation', description: 'Hold at temperature', parameters: [] }, { id: 'wash', name: 'Wash', type: 'wash', parameters: [] }, ] async function mountEditor(props: Record = {}) { const wrapper = mount(ProtocolStepEditor, { props: { templates, ...props } }) await nextTick() return wrapper } describe('ProtocolStepEditor (6c)', () => { it('should name the template picker by its label and its value', async () => { const wrapper = await mountEditor() const trigger = wrapper.get('.mint-searchable-select__trigger') const names = trigger.attributes('aria-labelledby')!.split(' ').map(id => wrapper.get(`[id="${id}"]`).text()) expect(names).toEqual(['Template', 'Incubation']) }) it.each([ ['Step name', 'input'], ['Description', 'textarea'], ])('should link the %s label to its %s', async (label, tag) => { const wrapper = await mountEditor() const labelEl = wrapper.findAll('label').find(item => item.text().startsWith(label))! expect(wrapper.get(`[id="${labelEl.attributes('for')}"]`).element.tagName.toLowerCase()).toBe(tag) }) it('should pick the template with SearchableSelect', async () => { const wrapper = await mountEditor() expect(wrapper.findComponent(SearchableSelect).props('modelValue')).toBe('inc') }) it('should offer each template with its description in the SearchableSelect', async () => { const wrapper = await mountEditor() expect(wrapper.findComponent(SearchableSelect).props('options')[0]).toMatchObject({ value: 'inc', label: 'Incubation', description: 'Hold at temperature', }) }) it('should switch the step to the template chosen in the SearchableSelect', async () => { const wrapper = await mountEditor() wrapper.findComponent(SearchableSelect).vm.$emit('update:modelValue', 'wash') await nextTick() expect(wrapper.findComponent(BaseInput).props('modelValue')).toBe('Wash') }) it('should name the mode in the title', async () => { const wrapper = await mountEditor({ mode: 'edit' }) expect(wrapper.get('.mint-protocol-editor__title').text()).toBe('Edit step') }) it('should not render a mode badge', async () => { const wrapper = await mountEditor({ mode: 'edit' }) expect(wrapper.find('[class*="mode-badge"]').exists()).toBe(false) }) it('should edit the description with BaseTextarea', async () => { const wrapper = await mountEditor() expect(wrapper.findComponent(BaseTextarea).exists()).toBe(true) }) it('should render the save action as a primary BaseButton', async () => { const wrapper = await mountEditor() const save = wrapper.findAllComponents(BaseButton).find((b) => b.text() === 'Add step') expect(save?.props('variant')).toBe('primary') }) it('should emit save with the step name typed into BaseInput', async () => { const wrapper = await mountEditor() await wrapper.findComponent(BaseInput).get('input').setValue('Warm up') await wrapper.findAllComponents(BaseButton).find((b) => b.text() === 'Add step')!.trigger('click') expect((wrapper.emitted('save')?.[0]?.[0] as { name: string }).name).toBe('Warm up') }) it('should size the template picker to the 32px control height', async () => { const wrapper = await mountEditor() expect(wrapper.findComponent(SearchableSelect).props('size')).toBe('sm') }) it('should size the step name field to the 32px control height', async () => { const wrapper = await mountEditor() expect(wrapper.findComponent(BaseInput).props('size')).toBe('sm') }) it('should show the step ordinal beside the title when stepNumber and stepCount are set', async () => { const wrapper = await mountEditor({ stepNumber: 3, stepCount: 7 }) expect(wrapper.find('.mint-protocol-editor__counter').text()).toBe('3 / 7') }) it('should hide the step ordinal when stepCount is not set', async () => { const wrapper = await mountEditor({ stepNumber: 3 }) expect(wrapper.find('.mint-protocol-editor__counter').exists()).toBe(false) }) })