/** * 计算公式 · 设计器集成测试。 * 覆盖:JSON 适配层往返(formula / overridable 不丢失)、公式编辑器实时校验与提交、 * 属性面板 emit 上抛(父级据此写入草稿)。 */ import { mount } from '@vue/test-utils' import { describe, expect, it } from 'vitest' import { editorToSchema, schemaToEditor, } from '@/views/module/module-management/components/design-editor/common/schemaModel' import FormulaEditor from '@/views/module/module-management/components/design-editor/common/FormulaEditor.vue' import FormInspector from '@/views/module/module-management/components/design-editor/form/FormInspector.vue' const FIELDS = [ { b_field: 'b_qty', b_name: '数量', b_type: 'number' }, { b_field: 'b_price', b_name: '单价', b_type: 'money' }, { b_field: 'b_amount', b_name: '金额', b_type: 'money' }, ] function editSchemaWithFormula(node = {}) { return { schemaVersion: 2, children: [ { type: 'row', children: [{ field: 'b_qty' }, { field: 'b_price' }] }, { type: 'row', children: [{ field: 'b_amount', readonly: true, ...node }], }, ], } } describe('公式设计器 · JSON 适配层', () => { it('加载时带出 formula / overridable,回写时原样保留', () => { const schema = editSchemaWithFormula({ formula: 'ROUND(b_qty * b_price, 2)', overridable: true, }) const { rows, groups } = schemaToEditor('edit', schema, 'mod1') const amountRow = rows.find((row) => row.b_field === 'b_amount') expect(amountRow.b_formula).toBe('ROUND(b_qty * b_price, 2)') expect(amountRow.b_overridable).toBe(1) const back = editorToSchema('edit', rows, groups, schema) const node = back.children[1].children[0] expect(node.formula).toBe('ROUND(b_qty * b_price, 2)') expect(node.overridable).toBe(true) }) it('清空公式时同时清掉 overridable,不留残属性', () => { const schema = editSchemaWithFormula({ formula: 'b_qty * 2', overridable: true }) const { rows, groups } = schemaToEditor('edit', schema, 'mod1') const amountRow = rows.find((row) => row.b_field === 'b_amount') amountRow.b_formula = '' amountRow.b_overridable = 0 const back = editorToSchema('edit', rows, groups, schema) const node = back.children[1].children[0] expect('formula' in node).toBe(false) expect('overridable' in node).toBe(false) }) }) describe('公式设计器 · 公式编辑器', () => { it('输入实时校验,失焦才提交给父级(撤销以一次落定的编辑为粒度)', async () => { const wrapper = mount(FormulaEditor, { props: { modelValue: '', fields: FIELDS }, }) const textarea = wrapper.find('textarea') // 只派发 input(setValue 会连带 change,测不出"提交时机"):输入过程不提交 textarea.element.value = 'b_qty * b_price' await textarea.trigger('input') expect(wrapper.emitted('update:modelValue')).toBeFalsy() expect(wrapper.text()).toContain('语法与字段引用检查通过') await textarea.trigger('change') expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['b_qty * b_price']) }) it('未知函数与未知字段给出可读错误', async () => { const wrapper = mount(FormulaEditor, { props: { modelValue: 'FOO(b_qty) + b_nope', fields: FIELDS }, }) const text = wrapper.text() expect(text).toContain('未知函数「FOO」') expect(text).toContain('字段「b_nope」不存在') }) it('子表阶段函数不可插入(面板中标注)', () => { const wrapper = mount(FormulaEditor, { props: { modelValue: '', fields: FIELDS } }) expect(wrapper.html()).toBeTruthy() expect(wrapper.text()).not.toContain('语法与字段引用检查通过') }) }) describe('公式设计器 · 属性面板', () => { it('公式与可覆写开关变更均 emit 上抛给父级写草稿', async () => { const wrapper = mount(FormInspector, { props: { selectedRow: { b_field: 'b_amount', b_colspan: 24, b_formula: 'b_qty * 2', b_overridable: 0, }, fieldName: () => '金额', controlTypeLabel: () => '金额', spanOf: () => 24, isTextarea: () => false, heightOf: () => 48, formula: 'b_qty * 2', formulaOverridable: false, fields: FIELDS, }, }) const editor = wrapper.findComponent(FormulaEditor) expect(editor.exists()).toBe(true) editor.vm.$emit('update:modelValue', 'b_qty * 3') expect(wrapper.emitted('formula-change')?.at(-1)).toEqual(['b_qty * 3']) }) })