Files
workspace/code/fms/fms-vue/tests/unit/module-query-config-panel.spec.js
T
2026-09-23 23:05:01 +08:00

157 lines
5.7 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.
/**
* 查询配置面板回写语义(module-management):
* - 草稿与 query JSON 不是一一映射(缺省 component、老结构 conditions 转 SQL、已废弃的
* filterGroups / defaultViewCode 被清理),只加载(含父级重载换新对象)不得回写,
* 否则草稿与库中基线不一致 → 切树节点误报「未保存」,并静默改写库中数据;
* - 用户真实编辑(改分组 / 改页签条件 SQL)必须回写,且回写为 schemaVersion 3 形态。
*/
import { beforeAll, beforeEach, describe, expect, it } from 'vitest'
import { mount } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import { nextTick } from 'vue'
import ModuleQueryConfigPanel from '@/views/module/module-management/ModuleQueryConfigPanel.vue'
import FmsTable from '@/components/fms-table/FmsTable.vue'
// jsdom 无布局:StkTable 在 clientWidth 为 0 时启用虚拟列裁剪,补成宽容器
beforeAll(() => {
Object.defineProperty(HTMLElement.prototype, 'clientWidth', {
configurable: true,
get() {
return 5000
},
})
})
const fields = [
{ b_field: 'b_status', b_name: '状态', b_type: 'select', b_canuse: 1 },
{ b_field: 'b_amount', b_name: '金额', b_type: 'money', b_canuse: 1 },
]
/** 库中历史查询配置(schemaVersion 2:条件为结构化 conditions、默认页签在顶层、还有 filterGroups) */
const legacySchema = {
schemaVersion: 2,
defaultViewCode: 'pending',
views: [
{
code: 'pending',
label: '待处理',
order: 10,
showCount: true,
conditions: [{ field: 'b_status', operator: 'eq', value: '1' }],
},
],
keywordSearch: { placeholder: '', trigger: 'enter_or_debounce', fields: [] },
quickFilters: [],
filterGroups: [{ code: 'basic', label: '基础', order: 10, fields: ['b_status'] }],
queryFields: { b_status: { component: 'select', operator: 'eq' } },
}
async function mountPanel(modelValue) {
const pinia = createPinia()
pinia.install({ provide() {}, config: { globalProperties: {} } })
setActivePinia(pinia)
const wrapper = mount(ModuleQueryConfigPanel, {
props: { moduleId: 'demo', fields, modelValue, loading: false },
global: { plugins: [pinia] },
attachTo: document.body,
})
await nextTick()
return wrapper
}
const lastSchema = (wrapper) => {
const events = wrapper.emitted('update:modelValue') || []
return events.length ? events[events.length - 1][0] : null
}
/** 切到「工作队列页签」分节并返回该分节内的页签表格(Tabs 懒渲染:未激活过的分节无 DOM) */
async function openViewsTab(wrapper) {
const tab = wrapper
.findAll('.tabs-tab')
.find((button) => button.text().includes('工作队列页签'))
await tab.trigger('click')
await nextTick()
const pane = wrapper.findAll('.tabs-pane').at(-1)
return pane.findComponent(FmsTable)
}
beforeEach(() => {
document.body.innerHTML = ''
})
describe('查询配置面板:加载不回写、编辑才回写', () => {
it('仅加载(含父级重载换新对象)不回写', async () => {
const wrapper = await mountPanel(legacySchema)
expect(lastSchema(wrapper)).toBe(null)
// 模拟父级重载:同一份历史 JSON 以新引用下发
await wrapper.setProps({ modelValue: JSON.parse(JSON.stringify(legacySchema)) })
await nextTick()
expect(lastSchema(wrapper)).toBe(null)
wrapper.unmount()
})
it('老结构条件在草稿里转成 SQL 展示,但加载本身不回写', async () => {
const wrapper = await mountPanel(legacySchema)
const rows = (await openViewsTab(wrapper)).props('dataSource')
expect(rows[0].conditionSql).toBe("[b_status] = N'1'")
expect(rows[0].isDefault).toBe(true)
expect(lastSchema(wrapper)).toBe(null)
wrapper.unmount()
})
it('改分组后回写为 schemaVersion 3(已废弃键被清理)', async () => {
const wrapper = await mountPanel(legacySchema)
const table = await openViewsTab(wrapper)
const rows = table.props('dataSource')
rows[0].group = '状态'
table.vm.$emit('change', rows[0], { dataIndex: 'group' })
await nextTick()
const next = lastSchema(wrapper)
expect(next).not.toBe(null)
expect(next.schemaVersion).toBe(3)
expect(next.views[0]).toMatchObject({
code: 'pending',
group: '状态',
isDefault: true,
conditionSql: "[b_status] = N'1'",
})
expect(next.filterGroups).toBeUndefined()
expect(next.defaultViewCode).toBeUndefined()
// 关键词只保留可搜索字段:旧的占位文案 / 触发方式随改写清理
expect(next.keywordSearch).toEqual({ fields: [] })
wrapper.unmount()
})
it('页签条件改为 SQL 后回写', async () => {
const wrapper = await mountPanel(legacySchema)
await openViewsTab(wrapper)
await wrapper.find('button.mm-qdesign__sql-cell').trigger('click')
await nextTick()
await wrapper.find('textarea').setValue("b_status = N'2'")
const buttons = wrapper.findAll('.fms-modal-footer button')
await buttons[buttons.length - 1].trigger('click')
await nextTick()
const next = lastSchema(wrapper)
expect(next).not.toBe(null)
expect(next.views[0].conditionSql).toBe("b_status = N'2'")
wrapper.unmount()
})
it('条件 SQL 含分号时不回写(拦截手误)', async () => {
const wrapper = await mountPanel(legacySchema)
await openViewsTab(wrapper)
await wrapper.find('button.mm-qdesign__sql-cell').trigger('click')
await nextTick()
await wrapper.find('textarea').setValue("b_status = N'2'; DELETE FROM x")
const buttons = wrapper.findAll('.fms-modal-footer button')
await buttons[buttons.length - 1].trigger('click')
await nextTick()
expect(lastSchema(wrapper)).toBe(null)
wrapper.unmount()
})
})