This commit is contained in:
oneao committed 2026-10-16 17:30:17 +08:00
1 parent e7c84a82a2
commit 025d3232c4
65 files changed
+9838 -9111

No files matched your search

@@ -5,9 +5,7 @@
* diff 成一条 insert),只有用户首次操作才落行;且 defineModel 写入后不能立刻
* 回读 prop(要下一轮才回传),所以建行后返回的是 reactive 代理行。
*
* 「启用」开关不在本面板内(已上提到页面级面板头),面板通过
* defineExpose 的 toggleEnabled() 拨动、enabled prop 接收状态,
* 测试走同一对口而不是找面板里的 G3Switch。
* 「启用」开关在表单第一行,测试点面板里真实的 G3Switch 驱动。
*/
import { describe, expect, it } from 'vitest'
@@ -42,27 +40,16 @@ function row(overrides: Partial<ModuleAutoCodeRow> = {}): ModuleAutoCodeRow {
}
/**
* 模拟父级 v-model:autocode + 面板头开关:emit 回来要显式写回 prop,
* 模拟父级 v-model:autocode:emit 回来要显式写回 prop,
* 否则复现不了「立刻回读旧值」的问题。
*
* 注意 enabled 用可变对象持有、不在 mount 期间引用 wrapper:
* 面板的 update:enabled 是 immediate 的,挂载时就会 emit,
* 处理器里访问尚未赋值的 wrapper 会直接抛 TDZ 错误。
*/
function mountPanel(autocode: ModuleAutoCodeRow[] = [], enabled = false) {
const state = { enabled }
function mountPanel(autocode: ModuleAutoCodeRow[] = []) {
const wrapper = mount(ModuleAutoCodePanel, {
props: {
moduleId: 'm1',
fields: FIELDS,
autocode,
get enabled() {
return state.enabled
},
'onUpdate:autocode': (value: ModuleAutoCodeRow[]) => wrapper.setProps({ autocode: value }),
'onUpdate:enabled': (value: boolean) => {
state.enabled = value
},
},
})
return wrapper
@@ -73,7 +60,7 @@ describe('自动编码面板', () => {
const wrapper = mountPanel()
// 模块从未配置自动编码:草稿为空,此时拨开关会走 ensureRow 建行
wrapper.vm.toggleEnabled(true)
await wrapper.find('[role="switch"]').trigger('click')
await wrapper.vm.$nextTick()
const emitted = wrapper.emitted('update:autocode')
@@ -90,7 +77,7 @@ describe('自动编码面板', () => {
const draft = [row({ b_canuse: 0, b_prefix: 'A' })]
const wrapper = mountPanel(draft)
wrapper.vm.toggleEnabled(true)
await wrapper.find('[role="switch"]').trigger('click')
await wrapper.vm.$nextTick()
// 已落草稿:直接改这一行的属性(父级 ref 是深度响应式,改得见)
@@ -102,9 +89,9 @@ describe('自动编码面板', () => {
it('把从未配置过的模块拨开又关掉,不留占位行(否则保存会 diff 出一条 insert)', async () => {
const wrapper = mountPanel()
wrapper.vm.toggleEnabled(true)
await wrapper.find('[role="switch"]').trigger('click')
await wrapper.vm.$nextTick()
wrapper.vm.toggleEnabled(false)
await wrapper.find('[role="switch"]').trigger('click')
await wrapper.vm.$nextTick()
// 最后一次写回的草稿必须回到空数组
@@ -114,9 +101,9 @@ describe('自动编码面板', () => {
it('停用既有规则时保留该行(那是既有规则的停用,要落库)', async () => {
const draft = [row({ b_canuse: 1 })]
const wrapper = mountPanel(draft, true)
const wrapper = mountPanel(draft)
wrapper.vm.toggleEnabled(false)
await wrapper.find('[role="switch"]').trigger('click')
await wrapper.vm.$nextTick()
expect(draft[0]?.b_canuse).toBe(0)
@@ -124,12 +111,14 @@ describe('自动编码面板', () => {
expect(wrapper.emitted('update:autocode')).toBeUndefined()
})
it('启用态回报给父级(驱动面板头开关)', async () => {
const wrapper = mountPanel([row({ b_canuse: 1 })])
await wrapper.vm.$nextTick()
it('开关的选中态跟随规则行(配过又停用的行显示为关)', async () => {
const on = mountPanel([row({ b_canuse: 1 })])
await on.vm.$nextTick()
expect(on.find('[role="switch"]').attributes('aria-checked')).toBe('true')
expect(wrapper.emitted('update:enabled')).toBeTruthy()
expect(wrapper.emitted('update:enabled')![0][0]).toBe(true)
const off = mountPanel([row({ b_canuse: 0 })])
await off.vm.$nextTick()
expect(off.find('[role="switch"]').attributes('aria-checked')).toBe('false')
})
it('紧凑网格固定竖排且标签左对齐,不跟随全局横排配置', () => {
@@ -139,10 +128,29 @@ describe('自动编码面板', () => {
expect(wrapper.findAll('.g3-form-item__label.is-label-right')).toHaveLength(0)
})
it('面板内不再自带「启用」开关(已上提到页面级面板头)', () => {
const wrapper = mountPanel([row()])
it('「启用」开关独占表单第一行,未配置时也可拨', () => {
const wrapper = mountPanel()
expect(wrapper.find('[role="switch"]').exists()).toBe(false)
// 标签顺序 = 填写顺序:行内从左到右、行间从上到下
// (启用 → 业务编号字段/重置周期 → 前缀/日期/分隔符/位数/起始值 → 编号预览)
const labels = wrapper.findAll('.g3-form-item__label')
expect(labels.map((item) => item.text())).toEqual([
'启用',
'业务编号字段',
'重置周期',
'前缀',
'日期',
'分隔符',
'位数',
'起始值',
'编号预览',
])
// 未配置(草稿为空)时开关也渲染、可拨:它就是配置入口
const switchEl = wrapper.find('[role="switch"]')
expect(switchEl.exists()).toBe(true)
expect(switchEl.attributes('disabled')).toBeUndefined()
expect(switchEl.attributes('aria-checked')).toBe('false')
})
it('预览按 前缀 + 日期 + 补位流水号 拼接', async () => {
@@ -157,13 +165,13 @@ describe('自动编码面板', () => {
})
it('启用态下的合法规则校验通过', async () => {
const wrapper = mountPanel([row()], true)
const wrapper = mountPanel([row()])
expect(await wrapper.vm.validate()).toEqual([])
})
it('启用但未选业务编号字段时报错(错误挂在对应 FormItem 上)', async () => {
const wrapper = mountPanel([row({ b_field: null })], true)
const wrapper = mountPanel([row({ b_field: null })])
const errors = await wrapper.vm.validate()
expect(errors.map((item) => item.name)).toContain('b_field')
@@ -171,7 +179,7 @@ describe('自动编码面板', () => {
})
it('位数超出 1-18 时报错', async () => {
const wrapper = mountPanel([row({ b_seqwidth: 20 })], true)
const wrapper = mountPanel([row({ b_seqwidth: 20 })])
const errors = await wrapper.vm.validate()
expect(errors.find((item) => item.name === 'b_seqwidth')?.errors[0]).toBe(
@@ -187,7 +195,7 @@ describe('自动编码面板', () => {
it('未启用时即使参数残缺也不报错(重置周期为空同样跳过)', async () => {
// 占位行 / 已停用的规则都不该拦保存:旧写法里 b_resettype 是硬性 required,会误报
const wrapper = mountPanel([row({ b_canuse: 0, b_resettype: '', b_seqwidth: 0 })], false)
const wrapper = mountPanel([row({ b_canuse: 0, b_resettype: '', b_seqwidth: 0 })])
expect(await wrapper.vm.validate()).toEqual([])
})