20260913231607
This commit is contained in:
1 parent
f3ad727f1c
commit
c31329d387
179 files changed
+10846
-2684
No files matched your search
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* FmsField 多态控件渲染器测试。
|
||||
* 覆盖契约:type→控件映射、checkbox 0/1↔boolean 归一、v-model 透传、
|
||||
* control-props 直达、fallthrough(class/原生事件)透传、change 转发、
|
||||
* 默认插槽逃生舱、types 注册表覆盖、selectId(FmsSelect) 双绑回显。
|
||||
*/
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { h, markRaw } from 'vue'
|
||||
import FmsField from '@/components/fms-field/FmsField.vue'
|
||||
import { Checkbox } from '@/components/ui'
|
||||
|
||||
// selectId 分支会挂载 FmsSelect,其依赖 @/services/api;桩掉避免真实请求
|
||||
vi.mock('@/services/api', () => ({
|
||||
pageDataApi: vi.fn(async () => ({ data: { rows: [], total: 0 } })),
|
||||
}))
|
||||
|
||||
describe('FmsField 类型 → 控件映射', () => {
|
||||
it('缺省 type 兜底渲染 Input', () => {
|
||||
const wrapper = mount(FmsField)
|
||||
expect(wrapper.find('.input-wrapper').exists()).toBe(true)
|
||||
expect(wrapper.find('input').exists()).toBe(true)
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('未知 type 兜底渲染 Input', () => {
|
||||
const wrapper = mount(FmsField, { props: { type: 'not-a-real-type' } })
|
||||
expect(wrapper.find('.input-wrapper').exists()).toBe(true)
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it.each([
|
||||
['textarea', 'textarea'],
|
||||
['number', 'input[inputmode="decimal"]'],
|
||||
['money', 'input[inputmode="decimal"]'],
|
||||
['checkbox', 'label.checkbox'],
|
||||
['select', '.select-trigger'],
|
||||
['date', '.fms-date-picker'],
|
||||
['datetime', '.fms-date-picker'],
|
||||
])('type=%s 渲染对应控件(%s)', (type, selector) => {
|
||||
const wrapper = mount(FmsField, { props: { type } })
|
||||
expect(wrapper.find(selector).exists()).toBe(true)
|
||||
wrapper.unmount()
|
||||
})
|
||||
})
|
||||
|
||||
describe('FmsField 值绑定', () => {
|
||||
it('input:v-model 双向透传', async () => {
|
||||
const wrapper = mount(FmsField, { props: { modelValue: 'a' } })
|
||||
expect(wrapper.find('input').element.value).toBe('a')
|
||||
await wrapper.find('input').setValue('abc')
|
||||
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['abc'])
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('checkbox:存储 1 → 勾选;取消勾选 → 抛 0', async () => {
|
||||
const wrapper = mount(FmsField, { props: { type: 'checkbox', modelValue: 1 } })
|
||||
const box = wrapper.find('input.checkbox-input')
|
||||
expect(box.element.checked).toBe(true)
|
||||
|
||||
await box.setValue(false)
|
||||
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual([0])
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('checkbox:存储 0 → 未勾选;勾选 → 抛 1', async () => {
|
||||
const wrapper = mount(FmsField, { props: { type: 'checkbox', modelValue: 0 } })
|
||||
const box = wrapper.find('input.checkbox-input')
|
||||
expect(box.element.checked).toBe(false)
|
||||
|
||||
await box.setValue(true)
|
||||
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual([1])
|
||||
wrapper.unmount()
|
||||
})
|
||||
})
|
||||
|
||||
describe('FmsField 扩展口子', () => {
|
||||
it('control-props 直达内部控件', () => {
|
||||
const wrapper = mount(FmsField, { props: { controlProps: { maxlength: 10 } } })
|
||||
expect(wrapper.find('input').attributes('maxlength')).toBe('10')
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('fallthrough class 合并到内部控件根节点', () => {
|
||||
const wrapper = mount(FmsField, { attrs: { class: 'my-field' } })
|
||||
expect(wrapper.find('.input-wrapper.my-field').exists()).toBe(true)
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('原生事件透传:@keydown 由内部控件冒泡命中', async () => {
|
||||
const onKeydown = vi.fn()
|
||||
const wrapper = mount(FmsField, { attrs: { onKeydown } })
|
||||
await wrapper.find('input').trigger('keydown', { key: 'Enter' })
|
||||
expect(onKeydown).toHaveBeenCalled()
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('内部控件 change 原样转发', async () => {
|
||||
const wrapper = mount(FmsField)
|
||||
await wrapper.find('input').trigger('change')
|
||||
expect(wrapper.emitted('change')).toBeTruthy()
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('默认插槽整体接管渲染(忽略 type)', () => {
|
||||
const wrapper = mount(FmsField, {
|
||||
props: { type: 'select' },
|
||||
slots: { default: () => h('div', { class: 'custom-control' }, '自定义') },
|
||||
})
|
||||
expect(wrapper.find('.custom-control').exists()).toBe(true)
|
||||
expect(wrapper.find('.select-trigger').exists()).toBe(false)
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('types 注册表覆盖内置类型映射', () => {
|
||||
const wrapper = mount(FmsField, {
|
||||
// 组件放入响应式容器需 markRaw,避免 Vue 告警(消费者注册自定义类型时同理)
|
||||
props: { type: 'input', types: { input: markRaw(Checkbox) } },
|
||||
})
|
||||
expect(wrapper.find('label.checkbox').exists()).toBe(true)
|
||||
expect(wrapper.find('.input-wrapper').exists()).toBe(false)
|
||||
wrapper.unmount()
|
||||
})
|
||||
})
|
||||
|
||||
describe('FmsField selectId(FmsSelect 双绑)', () => {
|
||||
it('渲染 FmsSelect,并用 label 回显选中名', () => {
|
||||
const wrapper = mount(FmsField, {
|
||||
props: {
|
||||
type: 'selectId',
|
||||
modelValue: '7',
|
||||
label: '客户甲',
|
||||
controlProps: { viewName: 'v_customer' },
|
||||
},
|
||||
})
|
||||
expect(wrapper.find('.fms-select').exists()).toBe(true)
|
||||
expect(wrapper.find('.select-input').element.value).toBe('客户甲')
|
||||
wrapper.unmount()
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user