Files
trans-form-component/test/transForm.test.js
T
2026-08-10 22:10:57 +08:00

302 lines
9.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { afterEach, describe, expect, it } from 'vitest';
import { defineComponent, h } from 'vue';
import { mount } from '@vue/test-utils';
import TransForm from '../src/TransForm.vue';
import HeadlessBetween from '../src/components/HeadlessBetween.vue';
import HeadlessDate from '../src/components/HeadlessDate.vue';
import HeadlessSelect from '../src/components/HeadlessSelect.vue';
import HeadlessTags from '../src/components/HeadlessTags.vue';
import { resetConfig, setConfig } from '../src/config.js';
// 每个用例后恢复全局配置,避免 setConfig 污染其它用例
afterEach(() => {
resetConfig();
});
describe('TransForm 操作符下拉(默认 query-option)', () => {
it('无 query-option 的字段渲染默认操作符列表(12 项,第一项 EQ)', () => {
const wrapper = mount(TransForm, {
props: { schema: [{ row: 12, field: 'name', 'field-name': '名称' }] },
});
const opts = wrapper.find('select').findAll('option');
expect(opts).toHaveLength(12);
expect(opts[0].text()).toBe('=');
expect(opts[0].attributes('value')).toBe('EQ');
expect(opts[11].text()).toBe('IS NOT NULL');
});
it('有 query-option 的字段仍渲染 schema 自己的选项(不被默认覆盖)', () => {
const wrapper = mount(TransForm, {
props: {
schema: [
{
row: 12,
field: 'status',
'field-name': '状态',
'query-option': [{ value: 'NE', label: '≠' }],
},
],
},
});
const opts = wrapper.find('select').findAll('option');
expect(opts).toHaveLength(1);
expect(opts[0].text()).toBe('≠');
expect(opts[0].attributes('value')).toBe('NE');
});
it('setConfig 自定义 queryOption 后,无 query-option 字段的下拉随之更新', () => {
setConfig({ queryOption: [{ value: 'GT', label: '>' }] });
const wrapper = mount(TransForm, {
props: { schema: [{ row: 12, field: 'age', 'field-name': '年龄' }] },
});
const opts = wrapper.find('select').findAll('option');
expect(opts).toHaveLength(1);
expect(opts[0].text()).toBe('>');
});
it('有 query-component 但无 query-option 的字段不套默认下拉(操作符由组件自己管理)', () => {
const wrapper = mount(TransForm, {
props: {
schema: [{ row: 12, field: 'custom', 'query-component': 'CustomQuery' }],
},
});
// 配置了 query-component 后不再渲染默认 select,未知组件走占位
expect(wrapper.find('select').exists()).toBe(false);
expect(wrapper.find('.tf-unknown').exists()).toBe(true);
});
});
describe('between-ui / in-ui(DESIGN Q32/Q33)', () => {
it('未配置 between-ui 且 query=BETWEEN 时默认渲染双 input(HeadlessBetween)', () => {
const wrapper = mount(TransForm, {
props: {
schema: [
{
row: 12,
field: 'age',
'field-name': '年龄',
'value-component': 'number',
'default-query': 'BETWEEN',
'query-option': [{ value: 'BETWEEN', label: 'BETWEEN' }],
},
],
},
});
expect(wrapper.findComponent(HeadlessBetween).exists()).toBe(true);
expect(wrapper.findAll('.tf-between-input')).toHaveLength(2);
});
it('未配置 in-ui 且 query=IN 时默认渲染 tags(HeadlessTags)', () => {
const wrapper = mount(TransForm, {
props: {
schema: [
{
row: 12,
field: 'tags',
'field-name': '标签',
'value-component': 'input',
'default-query': 'IN',
'query-option': [{ value: 'IN', label: 'IN' }],
},
],
},
});
expect(wrapper.findComponent(HeadlessTags).exists()).toBe(true);
});
it('between-ui 指定 date 时渲染 HeadlessDate', () => {
const wrapper = mount(TransForm, {
props: {
schema: [
{
row: 12,
field: 'age',
'field-name': '年龄',
'value-component': 'between',
'between-ui': 'date',
'default-query': 'BETWEEN',
'query-option': [{ value: 'BETWEEN', label: 'BETWEEN' }],
},
],
},
});
expect(wrapper.findComponent(HeadlessDate).exists()).toBe(true);
expect(wrapper.findComponent(HeadlessBetween).exists()).toBe(false);
expect(wrapper.find('input[type="date"]').exists()).toBe(true);
});
it('in-ui 指定 select 时渲染 HeadlessSelect', () => {
const wrapper = mount(TransForm, {
props: {
schema: [
{
row: 12,
field: 'status',
'field-name': '状态',
'value-component': 'tags',
'in-ui': 'select',
'default-query': 'IN',
options: [
{ value: 'a', label: 'A' },
{ value: 'b', label: 'B' },
],
'query-option': [{ value: 'IN', label: 'IN' }],
},
],
},
});
expect(wrapper.findComponent(HeadlessSelect).exists()).toBe(true);
expect(wrapper.findComponent(HeadlessTags).exists()).toBe(false);
});
it('非 BETWEEN/IN 时仍走 value-component,忽略 between-ui/in-ui', () => {
const wrapper = mount(TransForm, {
props: {
schema: [
{
row: 12,
field: 'age',
'value-component': 'number',
'between-ui': 'date',
'in-ui': 'select',
'default-query': 'EQ',
'query-option': [
{ value: 'EQ', label: '=' },
{ value: 'BETWEEN', label: 'BETWEEN' },
{ value: 'IN', label: 'IN' },
],
},
],
},
});
expect(wrapper.find('input[type="number"]').exists()).toBe(true);
expect(wrapper.findComponent(HeadlessDate).exists()).toBe(false);
expect(wrapper.findComponent(HeadlessSelect).exists()).toBe(false);
});
});
describe('query-component(DESIGN Q17/Q18)', () => {
it('无 query-component 时保持默认操作符下拉', () => {
const wrapper = mount(TransForm, {
props: {
schema: [
{
row: 12,
field: 'name',
'field-name': '名称',
'query-option': [
{ value: 'EQ', label: '=' },
{ value: 'LIKE', label: 'LIKE' },
],
},
],
},
});
const select = wrapper.find('select');
expect(select.exists()).toBe(true);
expect(select.findAll('option')).toHaveLength(2);
expect(wrapper.vm.getFilters()[0].query).toBe('EQ');
});
it('配置 query-component 时渲染自定义组件且 v-model 绑定 query', async () => {
const CustomQuery = defineComponent({
name: 'CustomQuery',
props: {
modelValue: { type: String, default: '' },
field: { type: String, default: '' },
fieldName: { type: String, default: '' },
queryOption: { type: Array, default: () => [] },
schema: { type: Object, default: () => ({}) },
disabled: { type: Boolean, default: false },
readOnly: { type: Boolean, default: false },
},
emits: ['update:modelValue'],
setup(props, { emit }) {
return () =>
h('button', {
class: 'custom-query-btn',
type: 'button',
disabled: props.disabled,
onClick: () => emit('update:modelValue', 'LIKE'),
}, `query:${props.modelValue}`);
},
});
const wrapper = mount(TransForm, {
props: {
schema: [
{
row: 12,
field: 'name',
'field-name': '名称',
'value-component': 'input',
'query-component': CustomQuery,
'default-query': 'EQ',
'query-option': [
{ value: 'EQ', label: '=' },
{ value: 'LIKE', label: 'LIKE' },
],
},
],
},
});
expect(wrapper.find('select').exists()).toBe(false);
const btn = wrapper.find('.custom-query-btn');
expect(btn.exists()).toBe(true);
expect(btn.text()).toBe('query:EQ');
expect(btn.attributes('disabled')).toBeUndefined();
await btn.trigger('click');
expect(wrapper.vm.getFilters()[0].query).toBe('LIKE');
expect(wrapper.find('.custom-query-btn').text()).toBe('query:LIKE');
});
it('query-component 字符串名可通过 componentMap 注册', async () => {
const CustomQuery = defineComponent({
name: 'MappedQuery',
inheritAttrs: false,
props: {
modelValue: { type: String, default: '' },
options: { type: Array, default: () => [] },
},
emits: ['update:modelValue'],
setup(props, { emit }) {
return () =>
h('select', {
class: 'mapped-query',
value: props.modelValue,
onChange: (e) => emit('update:modelValue', e.target.value),
}, [
h('option', { value: 'EQ' }, '='),
h('option', { value: 'GT' }, '>'),
]);
},
});
setConfig({ componentMap: { MappedQuery: CustomQuery } });
const wrapper = mount(TransForm, {
props: {
schema: [
{
row: 12,
field: 'age',
'query-component': 'MappedQuery',
'default-query': 'EQ',
'query-option': [
{ value: 'EQ', label: '=' },
{ value: 'GT', label: '>' },
],
},
],
},
});
const mapped = wrapper.find('.mapped-query');
expect(mapped.exists()).toBe(true);
await mapped.setValue('GT');
expect(wrapper.vm.getFilters()[0].query).toBe('GT');
});
});