import { describe, expect, it } from 'vitest'; import { mount } from '@vue/test-utils'; import HeadlessTextarea from '../src/components/HeadlessTextarea.vue'; import HeadlessSelect from '../src/components/HeadlessSelect.vue'; import HeadlessNumber from '../src/components/HeadlessNumber.vue'; import HeadlessDate from '../src/components/HeadlessDate.vue'; import HeadlessTags from '../src/components/HeadlessTags.vue'; import HeadlessBetween from '../src/components/HeadlessBetween.vue'; describe('HeadlessTextarea', () => { it('renders textarea and emits update:modelValue', async () => { const wrapper = mount(HeadlessTextarea, { props: { modelValue: 'hi' } }); expect(wrapper.find('textarea').element.value).toBe('hi'); await wrapper.find('textarea').setValue('hello'); expect(wrapper.emitted('update:modelValue')[0]).toEqual(['hello']); }); }); describe('HeadlessSelect', () => { const options = [ { value: 'EQ', label: '=' }, { value: 'GT', label: '>' }, ]; it('renders options from props.options', () => { const wrapper = mount(HeadlessSelect, { props: { modelValue: 'EQ', options } }); const opts = wrapper.findAll('option'); expect(opts).toHaveLength(2); expect(opts[0].text()).toBe('='); }); it('falls back to queryOption when options empty', () => { const wrapper = mount(HeadlessSelect, { props: { modelValue: 'EQ', queryOption: options, options: [] }, }); expect(wrapper.findAll('option')).toHaveLength(2); }); it('emits on change', async () => { const wrapper = mount(HeadlessSelect, { props: { modelValue: 'EQ', options } }); await wrapper.find('select').setValue('GT'); expect(wrapper.emitted('update:modelValue')[0]).toEqual(['GT']); }); }); describe('HeadlessNumber', () => { it('emits number input', async () => { const wrapper = mount(HeadlessNumber, { props: { modelValue: 3 } }); expect(wrapper.find('input').attributes('type')).toBe('number'); await wrapper.find('input').setValue('7'); expect(wrapper.emitted('update:modelValue')[0]).toEqual(['7']); }); }); describe('HeadlessDate', () => { it('renders date input', () => { const wrapper = mount(HeadlessDate, { props: { modelValue: '2026-08-07' } }); expect(wrapper.find('input').attributes('type')).toBe('date'); expect(wrapper.find('input').element.value).toBe('2026-08-07'); }); }); describe('HeadlessTags', () => { it('renders tags from comma string', () => { const wrapper = mount(HeadlessTags, { props: { modelValue: 'a,b,c' } }); expect(wrapper.findAll('.tf-tag')).toHaveLength(3); }); it('adds tag on Enter', async () => { const wrapper = mount(HeadlessTags, { props: { modelValue: 'a' } }); const input = wrapper.find('input'); await input.setValue('b'); await input.trigger('keydown', { key: 'Enter' }); expect(wrapper.emitted('update:modelValue').at(-1)).toEqual(['a,b']); }); it('removes tag on x click', async () => { const wrapper = mount(HeadlessTags, { props: { modelValue: 'a,b' } }); await wrapper.findAll('.tf-tag-x')[0].trigger('click'); expect(wrapper.emitted('update:modelValue').at(-1)).toEqual(['b']); }); it('respects disabled', async () => { const wrapper = mount(HeadlessTags, { props: { modelValue: 'a', disabled: true } }); await wrapper.find('input').trigger('keydown', { key: 'Enter' }); expect(wrapper.emitted('update:modelValue')).toBeUndefined(); }); }); describe('HeadlessBetween', () => { it('splits modelValue into two inputs', () => { const wrapper = mount(HeadlessBetween, { props: { modelValue: '1,10' } }); const inputs = wrapper.findAll('input'); expect(inputs).toHaveLength(2); expect(inputs[0].element.value).toBe('1'); expect(inputs[1].element.value).toBe('10'); }); it('emits combined string on start change', async () => { const wrapper = mount(HeadlessBetween, { props: { modelValue: '1,10' } }); await wrapper.findAll('input')[0].setValue('5'); expect(wrapper.emitted('update:modelValue').at(-1)).toEqual(['5,10']); }); });