Files
trans-form-component/test/controls.test.js
T
2026-08-07 11:11:40 +08:00

105 lines
4.0 KiB
JavaScript

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']);
});
});