/** * 查询配置面板(query JSON,schemaVersion 4):只配工作队列页签。 * * 这里测的是**结构与数据往返**:分组渲染、建组即建页签、编码自动派生、 * 组名就地改要改整组、旧的查询字段配置在回写时被清掉。 * G3Table 在 happy-dom 里没有真实尺寸,行内编辑与行拖拽是组件库自己的活, * 用桩替换掉(本面板只负责把列定义与数据源接上去)。 */ import { describe, expect, it } from 'vitest' import { mount } from '@vue/test-utils' import ModuleQueryPanel from '@/views/module/module-management/ModuleQueryPanel.vue' import type { SchemaJson } from '@/utils/schemaJson' function schema(views: Array> = []): SchemaJson { return { schemaVersion: 4, views } } /** * 模拟父级 v-model:query:emit 回来要显式写回 prop, * 否则复现不了「草稿回写 → 父级再传下来」这一轮往返。 */ function mountPanel(views: Array> = []) { const wrapper = mount(ModuleQueryPanel, { props: { query: schema(views), 'onUpdate:query': (value: SchemaJson) => wrapper.setProps({ query: value }), }, global: { stubs: { G3Table: true } }, }) return wrapper } /** 当前草稿里的页签行(面板对外就是这份 query JSON) */ function draftViews(wrapper: ReturnType): Array> { return (wrapper.props('query') as SchemaJson).views as Array> } function groupNames(wrapper: ReturnType): string[] { return wrapper.findAll('.mm-groups__name').map((el) => (el.element as HTMLInputElement).value) } /** 改组名:业务上走 change(失焦 / 回车提交),这里照同一条路触发 */ async function renameFirstGroup(wrapper: ReturnType, name: string) { const input = wrapper.find('.mm-groups__name') await input.setValue(name) await input.trigger('change') await wrapper.vm.$nextTick() } describe('查询配置面板(工作队列页签)', () => { it('按 group 归组:分组条上每组一个 chip,顺序跟着组内最小 order 走', () => { const wrapper = mountPanel([ { code: 'sail', label: '已开航', group: '状态', order: 30 }, { code: 'all', label: '全部', group: '状态', order: 10 }, { code: 'month', label: '本月', group: '时间', order: 20 }, ]) expect(groupNames(wrapper)).toEqual(['状态', '时间']) }) it('历史配置没写 group 的页签落到「默认」组(组从行反推,不存在无组的页签)', () => { const wrapper = mountPanel([{ code: 'all', label: '全部', order: 10 }]) expect(groupNames(wrapper)).toEqual(['默认']) }) it('新增页签组:建组的同时建出组内第一个页签,编码自动派生(空编码落库会被丢掉)', async () => { const wrapper = mountPanel() await wrapper.find('.mm-query__bar button').trigger('click') await wrapper.vm.$nextTick() expect(groupNames(wrapper)).toEqual(['分组 1']) const views = draftViews(wrapper) expect(views).toHaveLength(1) expect(views[0].group).toBe('分组 1') expect(views[0].code).toBe('view_1') // 组里第一个页签自动成为组内默认:一个组总要有一个默认选中的 expect(views[0].isDefault).toBe(true) }) it('再加一组时编码继续派生,不会与已有页签撞号', async () => { const wrapper = mountPanel([{ code: 'view_1', label: '全部', group: '状态', order: 10 }]) await wrapper.find('.mm-query__bar button').trigger('click') await wrapper.vm.$nextTick() expect(draftViews(wrapper)[1].code).toBe('view_2') }) it('组名就地改:改的是整组(组是共享实体,不能只改一行)', async () => { const wrapper = mountPanel([ { code: 'all', label: '全部', group: '状态', order: 10 }, { code: 'sail', label: '已开航', group: '状态', order: 20 }, ]) await renameFirstGroup(wrapper, '运行状态') expect(groupNames(wrapper)).toEqual(['运行状态']) expect(draftViews(wrapper).every((row) => row.group === '运行状态')).toBe(true) }) it('组名改成已存在的组名时拒绝(否则两个组会被无声合并)', async () => { const wrapper = mountPanel([ { code: 'a', label: '全部', group: '状态', order: 10 }, { code: 'b', label: '本月', group: '时间', order: 20 }, ]) await renameFirstGroup(wrapper, '时间') expect(groupNames(wrapper)).toEqual(['状态', '时间']) }) it('回写时把不再使用的 queryFields / keywordSearch / quickFilters 一并清掉', async () => { const wrapper = mountPanel() // 载入一份带旧配置的 schema:适配层只认 views,其余键由回写统一清理 await wrapper.setProps({ query: { schemaVersion: 3, views: [{ code: 'all', label: '全部', order: 10 }], keywordSearch: { fields: ['b_name'] }, quickFilters: [{ field: 'b_name', order: 10 }], queryFields: { b_name: { component: 'input', operator: 'like' } }, }, }) // 触发一次回写(新增一组) await wrapper.find('.mm-query__bar button').trigger('click') await wrapper.vm.$nextTick() const next = wrapper.props('query') as SchemaJson expect(next.queryFields).toBeUndefined() expect(next.keywordSearch).toBeUndefined() expect(next.quickFilters).toBeUndefined() expect(next.schemaVersion).toBe(4) }) })