Files
workspace/code/fms/fms-vue/tests/unit/module-dirty-sections.spec.js
T
oneao 4f89f373e6 feat(module-management): 导航状态化阶段一——分 section 脏检查 + tab 脏点
- useModuleConfig 新增 dirtySections(逐段 JSON.stringify 对比),hasModuleChanges
  改为其派生;先以整树对比语义为基准写等价断言(含「加载补全默认行即脏」的
  既有怪癖),新 spec module-dirty-sections 7 用例全绿
- index.vue #label 插槽给脏 tab 渲染 6px 圆点(title/aria-label,不只靠颜色),
  ui/Tabs 组件零改动;groups 由列表/表单两面板共享编辑,两 tab 联动标脏
- 计划文档 docs/module-management-tab-status-plan.md(横排保留、不做分组为已决策)
2026-08-30 20:06:36 +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/system/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)
})
})