20260927215912
This commit is contained in:
1 parent
27e49e2e45
commit
f22e67fc85
294 files changed
+29247
-3576
No files matched your search
@@ -0,0 +1,275 @@
|
||||
/**
|
||||
* 计算公式 · 运行期集成测试(FmsModuleEditModal)。
|
||||
* 覆盖:打开旧单据不重算、依赖变更触发重算、链式公式收敛、覆写与恢复、
|
||||
* 保存请求携带 moduleCode / formulaRevision / formulaOverrides。
|
||||
*/
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
const { saveObjectApi } = vi.hoisted(() => ({ saveObjectApi: vi.fn(async () => ({})) }))
|
||||
|
||||
vi.mock('@/services/api', () => ({
|
||||
loadDataApi: vi.fn(async (viewName, condition) => {
|
||||
// 第二个模块(b_order2)用于验证「被公式引用的隐藏字段(静默字段)」:条件里带模块编码即可区分
|
||||
const hidden = String(condition || '').includes('b_order2')
|
||||
if (viewName === 's_module') {
|
||||
return {
|
||||
data: [
|
||||
hidden
|
||||
? {
|
||||
b_id: 'b_order2',
|
||||
b_name: '订单2',
|
||||
b_module_type: 'data',
|
||||
b_canuse: 1,
|
||||
b_view_table: 'b_order2',
|
||||
b_save_table: 'b_order2',
|
||||
b_key_field: 'b_id',
|
||||
}
|
||||
: {
|
||||
b_id: 'b_order',
|
||||
b_name: '订单',
|
||||
b_module_type: 'data',
|
||||
b_canuse: 1,
|
||||
b_view_table: 'b_order',
|
||||
b_save_table: 'b_order',
|
||||
b_key_field: 'b_id',
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
if (viewName === 's_field') {
|
||||
return {
|
||||
data: hidden
|
||||
? [
|
||||
{ b_field: 'b_id', b_name: '单号', b_type: 'input', b_canuse: 1 },
|
||||
{ b_field: 'b_qty', b_name: '数量', b_type: 'number', b_canuse: 1 },
|
||||
{ b_field: 'b_price', b_name: '单价', b_type: 'money', b_canuse: 1 },
|
||||
{ b_field: 'b_amount', b_name: '金额', b_type: 'money', b_canuse: 1 },
|
||||
{ b_field: 'b_rate', b_name: '系数', b_type: 'number', b_canuse: 1 },
|
||||
]
|
||||
: [
|
||||
{ b_field: 'b_id', b_name: '单号', b_type: 'input', b_canuse: 1 },
|
||||
{ b_field: 'b_qty', b_name: '数量', b_type: 'number', b_canuse: 1 },
|
||||
{ b_field: 'b_price', b_name: '单价', b_type: 'money', b_canuse: 1 },
|
||||
{ b_field: 'b_amount', b_name: '金额', b_type: 'money', b_canuse: 1 },
|
||||
{ b_field: 'b_tax', b_name: '税额', b_type: 'money', b_canuse: 1 },
|
||||
],
|
||||
}
|
||||
}
|
||||
if (viewName === 's_module_schema') {
|
||||
return {
|
||||
data: [
|
||||
{
|
||||
b_module_id: hidden ? 'b_order2' : 'b_order',
|
||||
b_schema_type: 'edit',
|
||||
b_schema_json: JSON.stringify(
|
||||
hidden
|
||||
? {
|
||||
schemaVersion: 2,
|
||||
children: [
|
||||
{
|
||||
type: 'row',
|
||||
children: [
|
||||
{ field: 'b_qty', span: 12 },
|
||||
{ field: 'b_price', span: 12 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
children: [
|
||||
{
|
||||
field: 'b_amount',
|
||||
readonly: true,
|
||||
formula: 'ROUND(b_qty * b_price * b_rate, 2)',
|
||||
},
|
||||
],
|
||||
},
|
||||
// 隐藏的依赖字段:只取值、不渲染(静默字段)
|
||||
{ type: 'row', children: [{ field: 'b_rate', visible: false }] },
|
||||
],
|
||||
}
|
||||
: {
|
||||
schemaVersion: 2,
|
||||
children: [
|
||||
{
|
||||
type: 'group',
|
||||
id: 'g1',
|
||||
title: '基础信息',
|
||||
children: [
|
||||
{
|
||||
type: 'row',
|
||||
children: [
|
||||
{ field: 'b_qty', span: 12 },
|
||||
{ field: 'b_price', span: 12 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
children: [
|
||||
{
|
||||
field: 'b_amount',
|
||||
readonly: true,
|
||||
formula: 'ROUND(b_qty * b_price, 2)',
|
||||
},
|
||||
{
|
||||
field: 'b_tax',
|
||||
readonly: true,
|
||||
formula: 'b_amount * 0.1',
|
||||
overridable: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
return { data: [] }
|
||||
}),
|
||||
saveObjectApi,
|
||||
nextIdApi: vi.fn(async () => ({ data: '1' })),
|
||||
loadDataBySqlApi: vi.fn(async () => ({ data: [] })),
|
||||
}))
|
||||
|
||||
import FmsModuleEditModal from '@/components/fms-module-edit/FmsModuleEditModal.vue'
|
||||
|
||||
/** 弹窗内的输入框(按表单顺序):[b_qty, b_price, b_amount, b_tax] */
|
||||
function inputs() {
|
||||
return [...document.querySelectorAll('.fms-modal-body input')]
|
||||
}
|
||||
|
||||
/** 按索引写值并触发 input(v-model 口径),然后等一轮渲染 */
|
||||
async function setInput(index, value) {
|
||||
const input = inputs()[index]
|
||||
input.value = value
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }))
|
||||
await nextTick()
|
||||
}
|
||||
|
||||
function buttonByText(text) {
|
||||
return [...document.querySelectorAll('.fms-modal-footer button, .fms-modal-body button')].find(
|
||||
(button) => button.textContent.trim() === text,
|
||||
)
|
||||
}
|
||||
|
||||
async function openModal(record, dataCode = 'b_order') {
|
||||
const wrapper = mount(FmsModuleEditModal, {
|
||||
props: { open: false, dataCode, record },
|
||||
attachTo: document.body,
|
||||
})
|
||||
await wrapper.setProps({ open: true })
|
||||
await new Promise((resolve) => setTimeout(resolve, 40))
|
||||
await nextTick()
|
||||
return wrapper
|
||||
}
|
||||
|
||||
const STORED = { b_id: 'O1', b_qty: 9, b_price: '10.00', b_amount: '99', b_tax: '9.9' }
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
document.body.innerHTML = ''
|
||||
saveObjectApi.mockClear()
|
||||
})
|
||||
|
||||
describe('计算公式 · 运行期', () => {
|
||||
it('打开旧单据不重算,原有计算值原样保留', async () => {
|
||||
const wrapper = await openModal({ ...STORED })
|
||||
expect(document.querySelector('.fme-formula-banner')).toBeNull()
|
||||
expect(inputs()[2].value).toBe('99')
|
||||
expect(inputs()[3].value).toBe('9.9')
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('依赖字段变更触发重算,链式公式逐级收敛', async () => {
|
||||
const wrapper = await openModal({ ...STORED })
|
||||
await setInput(0, '2')
|
||||
expect(inputs()[2].value).toBe('20')
|
||||
expect(inputs()[3].value).toBe('2')
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('覆写后不再被公式覆盖,恢复公式回到计算结果', async () => {
|
||||
const wrapper = await openModal({ ...STORED })
|
||||
buttonByText('手工调整').click()
|
||||
await nextTick()
|
||||
await setInput(3, '5')
|
||||
|
||||
await setInput(0, '3')
|
||||
expect(inputs()[2].value).toBe('30')
|
||||
expect(inputs()[3].value).toBe('5') // 覆写值不被覆盖
|
||||
|
||||
buttonByText('恢复公式').click()
|
||||
await nextTick()
|
||||
expect(inputs()[3].value).toBe('3')
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('保存请求带 moduleCode / formulaRevision / formulaOverrides,且计算值进入更新', async () => {
|
||||
const wrapper = await openModal({ ...STORED })
|
||||
await setInput(0, '2')
|
||||
buttonByText('确定').click()
|
||||
await new Promise((resolve) => setTimeout(resolve, 20))
|
||||
|
||||
const [requests] = saveObjectApi.mock.calls.at(-1)
|
||||
const request = requests[0]
|
||||
expect(request.table).toBe('b_order')
|
||||
expect(request.moduleCode).toBe('b_order')
|
||||
expect(String(request.formulaRevision).startsWith('fnv1a64:')).toBe(true)
|
||||
expect(request.formulaOverrides).toBeUndefined()
|
||||
const updated = request.updates[0]
|
||||
expect(String(updated.b_qty)).toBe('2')
|
||||
expect(updated.b_amount).toBe('20')
|
||||
expect(updated.b_tax).toBe('2')
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('覆写声明随请求提交(不落业务字段)', async () => {
|
||||
const wrapper = await openModal({ ...STORED })
|
||||
buttonByText('手工调整').click()
|
||||
await nextTick()
|
||||
await setInput(3, '5')
|
||||
buttonByText('确定').click()
|
||||
await new Promise((resolve) => setTimeout(resolve, 20))
|
||||
|
||||
const [requests] = saveObjectApi.mock.calls.at(-1)
|
||||
expect(requests[0].formulaOverrides).toEqual(['b_tax'])
|
||||
expect(requests[0].updates[0].b_tax).toBe('5')
|
||||
wrapper.unmount()
|
||||
})
|
||||
})
|
||||
|
||||
describe('计算公式 · 静默字段(被引用的隐藏字段)', () => {
|
||||
it('隐藏依赖只取值不渲染,并参与重算', async () => {
|
||||
const wrapper = await openModal(
|
||||
{ b_id: 'O2', b_qty: 2, b_price: '10.00', b_amount: '10', b_rate: 0.5 },
|
||||
'b_order2',
|
||||
)
|
||||
// 隐藏字段不渲染:只有 数量 / 单价 / 金额 三个输入
|
||||
expect(inputs().length).toBe(3)
|
||||
await setInput(0, '4')
|
||||
expect(inputs()[2].value).toBe('20')
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('隐藏依赖为空时按空值参与运算,不阻断保存', async () => {
|
||||
const wrapper = await openModal(
|
||||
{ b_id: 'O2', b_qty: 2, b_price: '10.00', b_amount: '10' },
|
||||
'b_order2',
|
||||
)
|
||||
await setInput(0, '4')
|
||||
expect(inputs()[2].value).toBe('0')
|
||||
|
||||
buttonByText('确定').click()
|
||||
await new Promise((resolve) => setTimeout(resolve, 20))
|
||||
const [requests] = saveObjectApi.mock.calls.at(-1)
|
||||
expect(requests[0].moduleCode).toBe('b_order2')
|
||||
expect(requests[0].updates[0].b_amount).toBe('0')
|
||||
wrapper.unmount()
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user