Files
workspace/code/fms/fms-vue/tests/unit/module-dirty-sections.spec.js
T
2026-09-03 17:30:39 +08:00

139 lines
5.2 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.
/**
* 分 section 脏检查测试:守护 hasModuleChanges 的判定语义。
* 背景见 docs/module-management-tab-status-plan.md 阶段一:
* - 加载时自动补全的默认列表行只写 drafts 不写 originals(useModuleConfig.loadModuleConfig),
* 此时视为「脏」是既有行为,分 section 对比必须原样保留,不得“修复”;
* - hasModuleChanges 从整树 JSON.stringify 对比改为 dirtySections 派生,
* 本 spec 以判定结果等价为验收基准。
*/
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { ref } from 'vue'
import { useModuleConfig } from '@/views/module/module-management/composables/useModuleConfig'
vi.mock('@/services/api', () => ({
loadDataApi: vi.fn(),
describeApi: vi.fn(),
}))
vi.mock('@/components/ui', () => ({
Message: { success: vi.fn(), error: vi.fn(), warning: vi.fn(), info: vi.fn() },
}))
beforeEach(() => {
vi.clearAllMocks()
})
/** 完整的 8 段草稿结构(与 loadModuleConfig 构造的 original 同形) */
function baseConfig(over = {}) {
const config = {
module: { b_id: '1', b_code: 'demo', b_name: '演示模块', b_module_type: 'data' },
fields: [{ b_id: '10', b_field: 'amount', b_name: '金额', b_canuse: 1 }],
groups: [],
listConfig: [],
editConfig: [],
queryConfig: [],
autoCode: [],
powers: [],
}
return Object.assign(config, over)
}
function setup({ drafts, originals }) {
const draftsRef = ref(drafts)
const originalsRef = ref(originals)
const deps = {
moduleSelectedId: ref('1'),
moduleTreeData: ref([]),
findModuleNode: () => null,
selectedModule: ref({ b_id: '1', b_code: 'demo', b_name: '演示模块' }),
busy: ref(false),
configLoading: ref(false),
configSyncing: ref(false),
activeSection: ref('basic'),
loadedModuleId: ref('1'),
originals: originalsRef,
drafts: draftsRef,
skipModuleLoad: { value: false },
showConfirm: vi.fn(),
switchConfirm: ref(null),
i18nPanelRef: ref(null),
}
return { ...useModuleConfig(deps), originalsRef, draftsRef }
}
describe('useModuleConfig 脏检查', () => {
it('未修改时 hasModuleChanges 为 false', () => {
const originals = baseConfig()
const { hasModuleChanges } = setup({ drafts: JSON.parse(JSON.stringify(originals)), originals })
expect(hasModuleChanges.value).toBe(false)
})
it('无草稿时 hasModuleChanges 为 false', () => {
const { hasModuleChanges } = setup({ drafts: null, originals: null })
expect(hasModuleChanges.value).toBe(false)
})
it('任一 section 修改即 hasModuleChanges 为 true', () => {
const originals = baseConfig()
for (const key of ['module', 'fields', 'groups', 'listConfig', 'editConfig', 'queryConfig', 'autoCode', 'powers']) {
const drafts = JSON.parse(JSON.stringify(originals))
if (key === 'module') drafts.module.b_name = '改名'
else drafts[key] = [...drafts[key], { b_id: 'new' }]
const { hasModuleChanges } = setup({ drafts, originals })
expect(hasModuleChanges.value, `修改 ${key} 应判定为脏`).toBe(true)
}
})
it('加载时补全的默认列表行(只写 drafts)视为脏——既有行为', () => {
const originals = baseConfig()
const drafts = JSON.parse(JSON.stringify(originals))
drafts.listConfig = [{ b_id: 'tmp1', b_field_id: '10', b_visible: 1, b_xh: 10 }]
const { hasModuleChanges } = setup({ drafts, originals })
expect(hasModuleChanges.value).toBe(true)
})
it('查询模块缺失的 section(undefined)两侧一致时不脏,单侧出现即脏', () => {
const originals = {
module: { b_id: '2', b_code: 'q', b_module_type: 'query' },
fields: [],
groups: [],
listConfig: [],
editConfig: undefined,
queryConfig: [],
autoCode: undefined,
powers: [],
}
const same = setup({ drafts: JSON.parse(JSON.stringify(originals)), originals })
expect(same.hasModuleChanges.value).toBe(false)
const drafts = JSON.parse(JSON.stringify(originals))
drafts.editConfig = []
const diff = setup({ drafts, originals })
expect(diff.hasModuleChanges.value).toBe(true)
})
it('dirtySections 精确指出被修改的 section', () => {
const originals = baseConfig()
const drafts = JSON.parse(JSON.stringify(originals))
drafts.module.b_name = '改名'
drafts.queryConfig = [{ b_id: 'q1' }]
const { dirtySections } = setup({ drafts, originals })
expect(dirtySections.value.has('module')).toBe(true)
expect(dirtySections.value.has('queryConfig')).toBe(true)
expect(dirtySections.value.has('fields')).toBe(false)
expect(dirtySections.value.size).toBe(2)
})
it('保存后整体替换 originals/drafts 为同一份深拷贝,脏状态清空', () => {
const originals = baseConfig()
const drafts = JSON.parse(JSON.stringify(originals))
drafts.module.b_name = '改名'
const api = setup({ drafts, originals })
expect(api.hasModuleChanges.value).toBe(true)
// 模拟 useModuleSave 保存后的整体 cloneData 换引用
const saved = JSON.parse(JSON.stringify(drafts))
api.originalsRef.value = saved
api.draftsRef.value = JSON.parse(JSON.stringify(saved))
expect(api.hasModuleChanges.value).toBe(false)
})
})