255 lines
10 KiB
JavaScript
255 lines
10 KiB
JavaScript
import { mount } from '@vue/test-utils'
|
|
import { createPinia, setActivePinia } from 'pinia'
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
import FmsQueryControl from '@/components/fms-module-list/FmsQueryControl.vue'
|
|
import FmsQuickFilters from '@/components/fms-module-list/FmsQuickFilters.vue'
|
|
import FmsKeywordSearch from '@/components/fms-module-list/FmsKeywordSearch.vue'
|
|
import FmsActiveFilterPills from '@/components/fms-module-list/FmsActiveFilterPills.vue'
|
|
import FmsField from '@/components/fms-field/FmsField.vue'
|
|
import { resetUIConfig, setUIConfig } from '@/components/ui'
|
|
|
|
/** 构造一个查询条件项(字段 + 配置),供用例复用 */
|
|
const queryItem = (field, overrides = {}) => ({
|
|
field: { b_field: field, b_name: field, b_type: 'input' },
|
|
config: { b_operator: 'like', b_component: 'input', ...overrides },
|
|
})
|
|
|
|
/** 构造一个区间条件项:字段类型决定两端控件的形态 */
|
|
const rangeQueryItem = (bType) => ({
|
|
field: { b_field: 'created', b_type: bType },
|
|
config: { b_operator: 'between', b_component: bType },
|
|
})
|
|
|
|
describe('fms-module-list components', () => {
|
|
beforeEach(() => setActivePinia(createPinia()))
|
|
afterEach(() => resetUIConfig())
|
|
|
|
it('mounts a text query control with the new UI kit', () => {
|
|
const wrapper = mount(FmsQueryControl, {
|
|
props: {
|
|
item: {
|
|
field: { b_field: 'name', b_type: 'input' },
|
|
config: { b_operator: 'like', b_component: 'input' },
|
|
},
|
|
modelValue: '',
|
|
},
|
|
})
|
|
expect(wrapper.find('input').exists()).toBe(true)
|
|
})
|
|
|
|
it('快捷筛选条件字段用 FormItem 渲染,label 排版跟随全局布局配置', () => {
|
|
const mountFilters = () =>
|
|
mount(FmsQuickFilters, {
|
|
props: {
|
|
items: [queryItem('b_name')],
|
|
modelValue: {},
|
|
},
|
|
})
|
|
|
|
// 中文(默认):横排,label 在左
|
|
const zh = mountFilters()
|
|
const item = zh.find('.quick-field')
|
|
expect(item.classes()).toContain('fms-form-item')
|
|
expect(item.classes()).not.toContain('fms-form-item-vertical')
|
|
expect(item.find('.fms-form-item-label').text()).toBe('b_name')
|
|
// 查询区标签定宽(短标签也占满),同一行各条件的控件左缘因此对齐
|
|
expect(item.find('.fms-form-item-label').attributes('style')).toContain('width: 72px')
|
|
zh.unmount()
|
|
|
|
// 切到非中文:竖排,label 在上
|
|
setUIConfig({ form: { layout: 'vertical' } })
|
|
const en = mountFilters()
|
|
expect(en.find('.quick-field').classes()).toContain('fms-form-item-vertical')
|
|
en.unmount()
|
|
})
|
|
|
|
it('快捷筛选列数按条件占用收缩(普通条件 200px 一列,区间/日期跨两列)', () => {
|
|
const one = mount(FmsQuickFilters, {
|
|
props: { items: [queryItem('b_name')], modelValue: {} },
|
|
})
|
|
expect(one.find('.quick-grid').element.style.gridTemplateColumns).toBe(
|
|
'repeat(1, minmax(0, 200px))',
|
|
)
|
|
// 普通单值控件只占一列,不跨列
|
|
expect(one.find('.quick-field').element.style.gridColumn).toBe('')
|
|
one.unmount()
|
|
|
|
const four = mount(FmsQuickFilters, {
|
|
props: {
|
|
items: [queryItem('a'), queryItem('b'), queryItem('c'), queryItem('d')],
|
|
modelValue: {},
|
|
},
|
|
})
|
|
expect(four.find('.quick-grid').element.style.gridTemplateColumns).toBe(
|
|
'repeat(4, minmax(0, 200px))',
|
|
)
|
|
four.unmount()
|
|
|
|
// 日期区间(预设下拉 + 两端)跨两列:列数按占用列求和,宽控件不会被挤到下一行
|
|
const mixed = mount(FmsQuickFilters, {
|
|
props: { items: [rangeQueryItem('date'), queryItem('b_canuse')], modelValue: {} },
|
|
})
|
|
expect(mixed.find('.quick-grid').element.style.gridTemplateColumns).toBe(
|
|
'repeat(3, minmax(0, 200px))',
|
|
)
|
|
const [wide, narrow] = mixed.findAll('.quick-field')
|
|
expect(wide.element.style.gridColumn).toBe('span 2')
|
|
expect(narrow.element.style.gridColumn).toBe('')
|
|
mixed.unmount()
|
|
})
|
|
|
|
it('快捷筛选「查询」提交草稿、「重置」交给页面处理', async () => {
|
|
const wrapper = mount(FmsQuickFilters, {
|
|
props: { items: [queryItem('b_name')], modelValue: { b_name: 'abc' } },
|
|
})
|
|
await wrapper.find('button[type="button"]').trigger('click')
|
|
const emitted = wrapper.emitted('search')
|
|
expect(emitted).toHaveLength(1)
|
|
await wrapper.findAll('button').find((b) => b.text().includes('重置')).trigger('click')
|
|
expect(wrapper.emitted('reset')).toHaveLength(1)
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it('日期字段开启预设后先选语义区间,选「自定义」才落到两端控件', async () => {
|
|
const wrapper = mount(FmsQueryControl, {
|
|
props: { item: rangeQueryItem('date'), modelValue: { preset: 'this_month' }, datePresets: true },
|
|
})
|
|
const fields = wrapper.findAllComponents(FmsField)
|
|
// 预设态:只有一个下拉,没有两端日期
|
|
expect(fields).toHaveLength(1)
|
|
const presetField = fields[0]
|
|
expect(presetField.props('controlProps').options.map((o) => o.value)).toEqual([
|
|
'today',
|
|
'this_week',
|
|
'this_month',
|
|
'recent_3_months',
|
|
'custom',
|
|
])
|
|
// 切到自定义:值变区间、两端日期控件出现
|
|
presetField.vm.$emit('update:modelValue', 'custom')
|
|
expect(wrapper.emitted('update:modelValue').at(-1)[0]).toEqual(['', ''])
|
|
await wrapper.setProps({ modelValue: ['', ''] })
|
|
expect(wrapper.findAllComponents(FmsField)).toHaveLength(3)
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it('日期区间拆成两个独立控件加分隔符,控件类型随字段类型走', () => {
|
|
const dateRange = mount(FmsQueryControl, {
|
|
props: { item: rangeQueryItem('date'), modelValue: ['', ''] },
|
|
})
|
|
const dateFields = dateRange.findAllComponents(FmsField)
|
|
expect(dateFields.map((field) => field.props('type'))).toEqual(['date', 'date'])
|
|
// date 只到日,不需要覆盖控件自身的 format
|
|
expect(dateFields[0].props('controlProps').format).toBeUndefined()
|
|
expect(dateRange.find('.fms-query-control__separator').exists()).toBe(true)
|
|
dateRange.unmount()
|
|
|
|
const datetimeRange = mount(FmsQueryControl, {
|
|
props: { item: rangeQueryItem('datetime'), modelValue: ['', ''] },
|
|
})
|
|
const datetimeFields = datetimeRange.findAllComponents(FmsField)
|
|
expect(datetimeFields.map((field) => field.props('type'))).toEqual(['datetime', 'datetime'])
|
|
// datetime 端点必须到秒:分钟精度下「当天末」表达不出来,结束端默认时间会被归零成 23:59
|
|
expect(datetimeFields.map((field) => field.props('controlProps').format)).toEqual([
|
|
'YYYY-MM-DD HH:mm:ss',
|
|
'YYYY-MM-DD HH:mm:ss',
|
|
])
|
|
datetimeRange.unmount()
|
|
})
|
|
|
|
it('日期区间两端各自只受另一端约束,并带当天起 / 当天末的默认时间', () => {
|
|
const wrapper = mount(FmsQueryControl, {
|
|
props: { item: rangeQueryItem('date'), modelValue: ['2026-01-10', '2026-01-20'] },
|
|
})
|
|
const [start, end] = wrapper.findAllComponents(FmsField)
|
|
const startProps = start.props('controlProps')
|
|
const endProps = end.props('controlProps')
|
|
|
|
expect(startProps.timeDefault).toBe('start')
|
|
expect(endProps.timeDefault).toBe('end')
|
|
|
|
// 开始端:只禁晚于结束端,往前扩区间不受限
|
|
expect(startProps.disabledDate(new Date('2026-01-25'))).toBe(true)
|
|
expect(startProps.disabledDate(new Date('2026-01-05'))).toBe(false)
|
|
// 结束端:只禁早于开始端,往后扩区间不受限
|
|
expect(endProps.disabledDate(new Date('2026-01-05'))).toBe(true)
|
|
expect(endProps.disabledDate(new Date('2026-01-25'))).toBe(false)
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it('日期区间单端变更只写对应下标,另一端保持', () => {
|
|
const wrapper = mount(FmsQueryControl, {
|
|
props: { item: rangeQueryItem('date'), modelValue: ['2026-01-10', '2026-01-20'] },
|
|
})
|
|
const [start, end] = wrapper.findAllComponents(FmsField)
|
|
|
|
start.vm.$emit('update:modelValue', '2026-01-05')
|
|
expect(wrapper.emitted('update:modelValue').at(-1)[0]).toEqual(['2026-01-05', '2026-01-20'])
|
|
|
|
end.vm.$emit('update:modelValue', '2026-01-31')
|
|
expect(wrapper.emitted('update:modelValue').at(-1)[0]).toEqual(['2026-01-10', '2026-01-31'])
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it('数值区间仍是两个输入框加分隔符', () => {
|
|
const wrapper = mount(FmsQueryControl, {
|
|
props: { item: rangeQueryItem('number'), modelValue: ['', ''] },
|
|
})
|
|
expect(wrapper.findAll('input')).toHaveLength(2)
|
|
expect(wrapper.find('.fms-query-control__separator').exists()).toBe(true)
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it('关键词:回车立即查询,清空立即查询,防抖自动查询', async () => {
|
|
vi.useFakeTimers()
|
|
try {
|
|
const wrapper = mount(FmsKeywordSearch, {
|
|
props: { modelValue: '', placeholder: '搜索' },
|
|
})
|
|
const input = wrapper.find('input')
|
|
// 输入触发防抖
|
|
await input.setValue('东方')
|
|
expect(wrapper.emitted('search')).toBeUndefined()
|
|
await vi.advanceTimersByTimeAsync(400)
|
|
expect(wrapper.emitted('search').at(-1)[0]).toBe('东方')
|
|
// 回车立即查询(不等防抖)
|
|
await input.setValue('东方国际')
|
|
await input.trigger('keydown.enter')
|
|
expect(wrapper.emitted('search').at(-1)[0]).toBe('东方国际')
|
|
// 清空立即查询
|
|
await input.setValue('')
|
|
expect(wrapper.emitted('search').at(-1)[0]).toBe('')
|
|
wrapper.unmount()
|
|
} finally {
|
|
vi.useRealTimers()
|
|
}
|
|
})
|
|
|
|
it('条件回显:逐条展示并上抛删除与清除全部', async () => {
|
|
const wrapper = mount(FmsActiveFilterPills, {
|
|
props: {
|
|
pills: [
|
|
{ key: 'keyword', text: '关键词 东方' },
|
|
{ key: 'quick:b_status', text: '状态 属于 待处理' },
|
|
],
|
|
},
|
|
})
|
|
const pills = wrapper.findAll('.active-filter-pill')
|
|
expect(pills).toHaveLength(2)
|
|
expect(pills[0].text()).toContain('关键词 东方')
|
|
|
|
await pills[1].find('.active-filter-pill__remove').trigger('click')
|
|
expect(wrapper.emitted('remove')[0][0]).toBe('quick:b_status')
|
|
|
|
await wrapper.find('.active-filter-clear').trigger('click')
|
|
expect(wrapper.emitted('clear')).toHaveLength(1)
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it('无条件时条件回显区不渲染', () => {
|
|
const wrapper = mount(FmsActiveFilterPills, { props: { pills: [] } })
|
|
expect(wrapper.find('.fms-active-filters').exists()).toBe(false)
|
|
wrapper.unmount()
|
|
})
|
|
})
|