302 lines
9.5 KiB
JavaScript
302 lines
9.5 KiB
JavaScript
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');
|
||
});
|
||
});
|