518 lines
23 KiB
JavaScript
518 lines
23 KiB
JavaScript
/**
|
||
* 功能权限面板(用户管理 → 分配权限 → 功能权限)。
|
||
*
|
||
* 数据加载与保存由父级 UserPowerDrawer 负责,面板自身只做这几件事,测试只锁这几件:
|
||
* 1. 打开时的初始状态:只展开「全部分类」虚拟根,业务目录收起、不选中任何菜单;
|
||
* 2. 菜单选中语义:重复点击已选中的菜单不取消选中,只有点「全部分类」才清空;
|
||
* 3. 右侧结构:左右两栏用 Splitter 分栏(左树栏可拖拽调宽),
|
||
* 右栏是页头(菜单名 + 入口勾选框 + 右端 x/y 进度)+ 一个模块一块的授权块列表。
|
||
* 块头是模块(模块级勾选框 + 「已选 x/y」):勾选框表示"这个数据给没给"——有任意
|
||
* 一项权限即勾选、一项都没有才未勾选(不做半选),点一下即给模块使用 / 整块收回;
|
||
* 块内是它的动作,层级靠块头的上下关系;
|
||
* 没配权限点的模块不渲染;目录不渲染入口勾选框,说明放内容区中间而不是页头;
|
||
* 本页没有可配置模块时内容区中间给一句说明(目录与外链 / 自定义页文案不同);
|
||
* 4. 勾选结果经 v-model 回传,三级是「逐级给、连带收」:
|
||
* 勾下级自动补上级(操作 → 模块使用 → 入口);勾上级不替下级勾选;
|
||
* 取消上级连带收回下级(关入口收回本页全部,取消模块收回该模块全部权限点)。
|
||
*
|
||
* 勾选框是 ui/Checkbox(label 包原生 input),测试直接驱动 input 的 change ——
|
||
* jsdom 不保证 label 点击会转发给内部控件,走 change 才是组件真实监听的入口。
|
||
*
|
||
* 账号级的批量操作(全授权 / 全禁用 / 清空权限)不在本面板,由 user-power-drawer.spec.js 覆盖。
|
||
*/
|
||
import { describe, it, expect, beforeEach } from 'vitest'
|
||
import { mount } from '@vue/test-utils'
|
||
import { nextTick } from 'vue'
|
||
import UserPowerPanel from '@/views/system/user/UserPowerPanel.vue'
|
||
import { clearMessages } from '@/stores/i18n'
|
||
|
||
/** 菜单树夹具:顺序模拟 s_menu 的 b_depth / b_xh 排序(父目录在前) */
|
||
const MENU_TREE = [
|
||
{ b_id: 'base', b_parent_id: null, b_name: '基础资料', b_menu_type: 'directory', b_xh: 10 },
|
||
{ b_id: 'system', b_parent_id: null, b_name: '系统管理', b_menu_type: 'directory', b_xh: 20 },
|
||
{ b_id: 'partner', b_parent_id: 'base', b_name: '合作伙伴', b_menu_type: 'page', b_xh: 10 },
|
||
// 外链菜单:不绑模块,入口就是唯一的授权单元
|
||
{ b_id: 'portal', b_parent_id: 'base', b_name: '外部系统', b_menu_type: 'external', b_xh: 20 },
|
||
{ b_id: 'user', b_parent_id: 'system', b_name: '用户管理', b_menu_type: 'page', b_xh: 10 },
|
||
]
|
||
|
||
const MENU_MODULES = {
|
||
// 合作伙伴菜单绑两个模块,动作集不同 → 验证动作各自成组、互不影响
|
||
partner: [
|
||
{ b_menu_id: 'partner', b_module_id: 'v_b_othercompany', b_xh: 10 },
|
||
{ b_menu_id: 'partner', b_module_id: 'v_b_contact', b_xh: 20 },
|
||
],
|
||
// 用户管理绑两个模块,其中币种(b_money)没配权限点 → 验证「未配置权限点的模块不渲染」
|
||
user: [
|
||
{ b_menu_id: 'user', b_module_id: 'v_b_user', b_xh: 10 },
|
||
{ b_menu_id: 'user', b_module_id: 'b_money', b_xh: 20 },
|
||
],
|
||
}
|
||
|
||
const MODULE_BY_ID = new Map([
|
||
['v_b_othercompany', { b_id: 'v_b_othercompany', b_name: '合作伙伴' }],
|
||
['v_b_contact', { b_id: 'v_b_contact', b_name: '联系人' }],
|
||
['v_b_user', { b_id: 'v_b_user', b_name: '用户管理' }],
|
||
['b_money', { b_id: 'b_money', b_name: '币种' }],
|
||
])
|
||
|
||
/** 每个模块的权限点:access 是「模块使用」(并入模块级勾选框),其余是「动作」列 */
|
||
const POWER_BY_MODULE = {
|
||
v_b_othercompany: [
|
||
{ b_id: 'module.v_b_othercompany', b_name: '模块使用', b_action: 'access' },
|
||
{ b_id: 'action.v_b_othercompany.create', b_name: '新增', b_action: 'create' },
|
||
{ b_id: 'action.v_b_othercompany.update', b_name: '修改', b_action: 'update' },
|
||
{ b_id: 'action.v_b_othercompany.audit', b_name: '审核', b_action: 'audit' },
|
||
],
|
||
// 联系人只有新增 / 删除:动作列也只剩这两项
|
||
v_b_contact: [
|
||
{ b_id: 'module.v_b_contact', b_name: '模块使用', b_action: 'access' },
|
||
{ b_id: 'action.v_b_contact.create', b_name: '新增', b_action: 'create' },
|
||
{ b_id: 'action.v_b_contact.delete', b_name: '删除', b_action: 'delete' },
|
||
],
|
||
v_b_user: [{ b_id: 'module.v_b_user', b_name: '模块使用', b_action: 'access' }],
|
||
}
|
||
|
||
/** 合作伙伴模块(v_b_othercompany)的权限点全量:模块级勾选框勾上时就该是这些 */
|
||
const OTHERCOMPANY_ALL = [
|
||
'module.v_b_othercompany',
|
||
'action.v_b_othercompany.create',
|
||
'action.v_b_othercompany.update',
|
||
'action.v_b_othercompany.audit',
|
||
]
|
||
|
||
beforeEach(() => {
|
||
localStorage.clear()
|
||
sessionStorage.clear()
|
||
clearMessages()
|
||
})
|
||
|
||
function mountPanel(props = {}) {
|
||
return mount(UserPowerPanel, {
|
||
props: {
|
||
modelValue: [],
|
||
menuTree: MENU_TREE,
|
||
menuModules: MENU_MODULES,
|
||
moduleById: MODULE_BY_ID,
|
||
powerByModule: POWER_BY_MODULE,
|
||
...props,
|
||
},
|
||
})
|
||
}
|
||
|
||
/** 选中一个菜单(打开时不再默认选中,多数用例只需把右侧切到目标菜单) */
|
||
async function mountWithMenu(menuId, props = {}) {
|
||
const wrapper = mountPanel(props)
|
||
wrapper.vm.selectedMenuId = menuId
|
||
await nextTick()
|
||
return wrapper
|
||
}
|
||
|
||
/** 按标题文本找树行(每次重新查询,避免拿着已过期的 DOM 包装器做断言) */
|
||
function findRow(wrapper, text) {
|
||
return wrapper.findAll('.tree-node').find((row) => row.text().includes(text))
|
||
}
|
||
|
||
/** 授权块的模块名清单(按块顺序) */
|
||
function moduleNames(wrapper) {
|
||
return wrapper.findAll('.power-row__head .checkbox-label').map((el) => el.text())
|
||
}
|
||
|
||
/** 页头(菜单名 + 入口)的入口勾选框(目录没有入口权限点时返回 undefined) */
|
||
function entryCheck(wrapper) {
|
||
const found = wrapper.find('.power-head__entry')
|
||
return found.exists() ? found : undefined
|
||
}
|
||
|
||
/** 第 index 个授权块的模块级勾选框 */
|
||
function moduleCheck(wrapper, index = 0) {
|
||
return wrapper.findAll('.power-row__head .checkbox')[index]
|
||
}
|
||
|
||
/** 勾选框形态的开关总数:页头入口 1 个 + 每块模块 1 个(动作勾选框在块内,不计入) */
|
||
function useChecks(wrapper) {
|
||
return wrapper.findAll('.power-head__entry, .power-row__head .checkbox')
|
||
}
|
||
|
||
/** 第 index 个授权块块头的「已选」计数(没有动作的模块不渲染该节点) */
|
||
function rowCount(wrapper, index = 0) {
|
||
return wrapper.findAll('.power-row .power-row__count')[index]
|
||
}
|
||
|
||
/** 某个授权块(按模块名定位,比按下标稳) */
|
||
function moduleRow(wrapper, moduleName) {
|
||
return wrapper
|
||
.findAll('.power-row')
|
||
.find((row) => row.find('.power-row__head .checkbox-label').text() === moduleName)
|
||
}
|
||
|
||
/**
|
||
* 动作勾选框:按「模块名 + 动作名」定位。
|
||
* 动作只出现在所属模块的块内,所以按名字找比按下标找稳,动作数量变化也不影响。
|
||
*/
|
||
function actionCheck(wrapper, moduleName, actionName) {
|
||
return moduleRow(wrapper, moduleName)
|
||
.findAll('.power-row__actions .checkbox')
|
||
.find((el) => el.find('.checkbox-label').text() === actionName)
|
||
}
|
||
|
||
/** 某个模块的动作名清单(按铺排顺序) */
|
||
function actionNames(wrapper, moduleName) {
|
||
return moduleRow(wrapper, moduleName)
|
||
.findAll('.power-row__actions .checkbox-label')
|
||
.map((el) => el.text())
|
||
}
|
||
|
||
/** 驱动 ui/Checkbox 内部的原生 input:勾上 / 取消 */
|
||
function setCheck(checkbox, checked = true) {
|
||
return checkbox.find('.checkbox-input').setValue(checked)
|
||
}
|
||
|
||
/** 回传的勾选结果(取最后一次 emit) */
|
||
function lastEmitted(wrapper) {
|
||
const events = wrapper.emitted('update:modelValue')
|
||
return events[events.length - 1][0]
|
||
}
|
||
|
||
describe('功能权限面板:打开时的初始状态', () => {
|
||
it('只展开「全部分类」,业务目录收起、不选中任何菜单', async () => {
|
||
const wrapper = mountPanel({ menuTree: [] })
|
||
await nextTick()
|
||
await wrapper.setProps({ menuTree: MENU_TREE })
|
||
await nextTick()
|
||
|
||
expect(wrapper.vm.selectedMenuId).toBe('')
|
||
// 业务目录的展开键不由面板补写:虚拟根的展开由 FmsTree 承担
|
||
expect(wrapper.vm.expandedKeys).toEqual([])
|
||
expect(findRow(wrapper, '全部分类')).toBeTruthy()
|
||
expect(findRow(wrapper, '基础资料')).toBeTruthy()
|
||
// 目录收起 → 其下的页面菜单不可见
|
||
expect(findRow(wrapper, '合作伙伴')).toBeUndefined()
|
||
expect(wrapper.find('.power-panel__main').text()).toContain('请选择左侧菜单')
|
||
})
|
||
})
|
||
|
||
describe('功能权限面板:菜单选中语义', () => {
|
||
it('重复点击已选中的菜单:保持选中,不跳回「全部分类」', async () => {
|
||
const wrapper = mountPanel()
|
||
await nextTick()
|
||
|
||
// 展开目录 → 选中「合作伙伴」→ 再点一次(ui/Tree 会传出空 keys,即「取消选中」)
|
||
await findRow(wrapper, '基础资料').find('.tree-node-switcher').trigger('click')
|
||
await nextTick()
|
||
await findRow(wrapper, '合作伙伴').find('.tree-node-content').trigger('click')
|
||
await nextTick()
|
||
expect(wrapper.vm.selectedMenuId).toBe('partner')
|
||
|
||
await findRow(wrapper, '合作伙伴').find('.tree-node-content').trigger('click')
|
||
await nextTick()
|
||
|
||
expect(wrapper.vm.selectedMenuId).toBe('partner')
|
||
expect(findRow(wrapper, '合作伙伴').classes()).toContain('is-selected')
|
||
expect(findRow(wrapper, '全部分类').classes()).not.toContain('is-selected')
|
||
})
|
||
|
||
it('点「全部分类」虚拟根:清空选中,右侧回到空态提示', async () => {
|
||
const wrapper = mountPanel()
|
||
await nextTick()
|
||
|
||
await findRow(wrapper, '基础资料').find('.tree-node-switcher').trigger('click')
|
||
await nextTick()
|
||
await findRow(wrapper, '合作伙伴').find('.tree-node-content').trigger('click')
|
||
await nextTick()
|
||
expect(wrapper.vm.selectedMenuId).toBe('partner')
|
||
|
||
await findRow(wrapper, '全部分类').find('.tree-node-content').trigger('click')
|
||
await nextTick()
|
||
|
||
expect(wrapper.vm.selectedMenuId).toBe('')
|
||
expect(wrapper.find('.power-panel__main').text()).toContain('请选择左侧菜单')
|
||
})
|
||
|
||
it('点另一个菜单:选中切到该菜单', async () => {
|
||
const wrapper = mountPanel()
|
||
await nextTick()
|
||
|
||
// 「用户管理」在收起的「系统管理」下:先展开目录再点
|
||
await findRow(wrapper, '系统管理').find('.tree-node-switcher').trigger('click')
|
||
await nextTick()
|
||
await findRow(wrapper, '用户管理').find('.tree-node-content').trigger('click')
|
||
await nextTick()
|
||
|
||
expect(wrapper.vm.selectedMenuId).toBe('user')
|
||
expect(moduleNames(wrapper)).toEqual(['用户管理'])
|
||
})
|
||
})
|
||
|
||
describe('功能权限面板:右侧结构', () => {
|
||
it('左右两栏用 Splitter 分栏:左栏菜单树可拖拽调宽,右栏吃满剩余宽度', async () => {
|
||
const wrapper = await mountPanel()
|
||
await nextTick()
|
||
|
||
expect(wrapper.find('.power-panel__body.fms-splitter').exists()).toBe(true)
|
||
expect(wrapper.find('.power-panel__body .fms-splitter-bar').exists()).toBe(true)
|
||
|
||
const panels = wrapper.find('.power-panel__body').findAll('.fms-splitter-panel')
|
||
expect(panels).toHaveLength(2)
|
||
expect(panels[0].find('.fms-tree-panel').exists()).toBe(true)
|
||
expect(panels[1].find('.power-panel__main').exists()).toBe(true)
|
||
})
|
||
|
||
it('页头:菜单名做标题 + 入口勾选框 + 右端的授权进度', async () => {
|
||
const wrapper = await mountWithMenu('partner')
|
||
|
||
expect(wrapper.find('.power-head__name').text()).toBe('合作伙伴')
|
||
expect(entryCheck(wrapper).find('.checkbox-label').text()).toBe('可访问')
|
||
// 进度常显的只是两个数字,完整说法在 title 里
|
||
expect(wrapper.find('.power-head__meta').text()).toBe('0/2')
|
||
expect(wrapper.find('.power-head__meta').attributes('title')).toBe(
|
||
'本页 2 个模块,已授权 0 个',
|
||
)
|
||
// 页头里只有菜单名、勾选框与进度,没有多余的说明句
|
||
expect(wrapper.find('.power-head').text()).toBe('合作伙伴 可访问 0/2')
|
||
})
|
||
|
||
it('本页进度:至少给了一项权限的模块才计入已授权', async () => {
|
||
const wrapper = await mountWithMenu('partner', {
|
||
modelValue: ['menu.partner', 'module.v_b_othercompany', 'action.v_b_othercompany.create'],
|
||
})
|
||
|
||
expect(wrapper.find('.power-head__meta').text()).toBe('1/2')
|
||
})
|
||
|
||
it('入口的连带影响只进 title,不常显在页头', async () => {
|
||
const wrapper = await mountWithMenu('partner')
|
||
|
||
expect(entryCheck(wrapper).attributes('title')).toBe(
|
||
'可访问「合作伙伴」(打开只给入口,本页模块与操作要自己勾;关闭会全部收回)',
|
||
)
|
||
})
|
||
|
||
it('授权块:块头是模块(勾选框 + 已选),块内是该模块的动作', async () => {
|
||
const wrapper = await mountWithMenu('partner')
|
||
|
||
expect(moduleNames(wrapper)).toEqual(['合作伙伴', '联系人'])
|
||
expect(wrapper.findAll('.power-row')).toHaveLength(2)
|
||
// 模块在上、动作在下:块头(.power-row__head)里的勾选框只有模块级那一个
|
||
expect(wrapper.findAll('.power-row__head .checkbox')).toHaveLength(2)
|
||
// 勾选框形态的开关:页头入口 1 个 + 模块级 2 个(动作勾选框在块内,不计入)
|
||
expect(useChecks(wrapper)).toHaveLength(3)
|
||
})
|
||
|
||
it('动作按模块分行:每行的动作只属于该行模块,互不挤占', async () => {
|
||
const wrapper = await mountWithMenu('partner')
|
||
|
||
expect(actionNames(wrapper, '合作伙伴')).toEqual(['新增', '修改', '审核'])
|
||
expect(actionNames(wrapper, '联系人')).toEqual(['新增', '删除'])
|
||
})
|
||
|
||
it('动作勾选框挂 title(模块名 + 动作名),名称超长被省略时可查看全称', async () => {
|
||
const wrapper = await mountWithMenu('partner')
|
||
expect(actionCheck(wrapper, '合作伙伴', '新增').attributes('title')).toBe('合作伙伴 新增')
|
||
})
|
||
|
||
it('块头「已选 x/y」= 该模块已授权的动作数 / 动作总数', async () => {
|
||
const wrapper = await mountWithMenu('partner', {
|
||
modelValue: ['module.v_b_othercompany', 'action.v_b_othercompany.create'],
|
||
})
|
||
|
||
// 「模块使用」不计入已选:它并入模块级勾选框,与块内的动作不是一回事
|
||
expect(rowCount(wrapper, 0).text()).toBe('已选 1/3')
|
||
expect(rowCount(wrapper, 1).text()).toBe('已选 0/2')
|
||
})
|
||
|
||
it('只有模块使用、没有动作的模块不显示计数', async () => {
|
||
const wrapper = await mountWithMenu('user')
|
||
|
||
expect(moduleNames(wrapper)).toEqual(['用户管理'])
|
||
expect(rowCount(wrapper, 0)).toBeUndefined()
|
||
})
|
||
|
||
it('模块级勾选框两态:有任意一项权限就勾选,一项都没有才未勾选', async () => {
|
||
const wrapper = await mountWithMenu('partner')
|
||
|
||
// 全给:模块使用 + 全部动作都在 → 勾选
|
||
await wrapper.setProps({ modelValue: OTHERCOMPANY_ALL })
|
||
expect(moduleCheck(wrapper, 0).classes()).toContain('checkbox-checked')
|
||
expect(moduleCheck(wrapper, 0).classes()).not.toContain('checkbox-indeterminate')
|
||
|
||
// 只勾了一个动作 → 数据权限也已给(勾动作会补模块使用),勾选框照样是勾选
|
||
await wrapper.setProps({ modelValue: ['action.v_b_othercompany.create'] })
|
||
expect(moduleCheck(wrapper, 0).classes()).toContain('checkbox-checked')
|
||
|
||
// 只留了模块使用、一个动作都没给:这个数据仍算有权限 → 勾选
|
||
await wrapper.setProps({ modelValue: ['module.v_b_othercompany'] })
|
||
expect(moduleCheck(wrapper, 0).classes()).toContain('checkbox-checked')
|
||
expect(moduleCheck(wrapper, 0).classes()).not.toContain('checkbox-indeterminate')
|
||
|
||
// 一项都没有 → 未勾选(不做半选,没有减号这个形态)
|
||
await wrapper.setProps({ modelValue: [] })
|
||
expect(moduleCheck(wrapper, 0).classes()).not.toContain('checkbox-checked')
|
||
expect(moduleCheck(wrapper, 0).classes()).not.toContain('checkbox-indeterminate')
|
||
})
|
||
|
||
it('模块级勾选框的 title 说清点它是给数据权限还是整块收回', async () => {
|
||
const wrapper = await mountWithMenu('partner')
|
||
|
||
expect(moduleCheck(wrapper, 0).attributes('title')).toBe('给「合作伙伴」模块权限(不含操作)')
|
||
|
||
await wrapper.setProps({ modelValue: OTHERCOMPANY_ALL })
|
||
expect(moduleCheck(wrapper, 0).attributes('title')).toBe(
|
||
'取消「合作伙伴」的全部授权(含全部操作)',
|
||
)
|
||
})
|
||
|
||
it('已授权的入口渲染为打开态', async () => {
|
||
const wrapper = await mountWithMenu('partner', { modelValue: ['menu.partner'] })
|
||
|
||
expect(wrapper.vm.menuPowerCode).toBe('menu.partner')
|
||
expect(entryCheck(wrapper).classes()).toContain('checkbox-checked')
|
||
})
|
||
|
||
it('目录:不渲染入口勾选框(没有 menu.* 权限点),说明放在内容区中间', async () => {
|
||
const wrapper = await mountWithMenu('base')
|
||
|
||
expect(useChecks(wrapper)).toHaveLength(0)
|
||
// 目录没有可勾的入口:页头只显示名称,不出现勾选框,也不在页头写说明
|
||
// (说明会撑高页头,与页面菜单的页头高度对不齐)
|
||
expect(entryCheck(wrapper)).toBeUndefined()
|
||
expect(wrapper.find('.power-head__name').text()).toBe('基础资料')
|
||
expect(wrapper.find('.power-head').text()).toBe('基础资料')
|
||
expect(wrapper.find('.power-panel__state').text()).toBe('目录不需要单独授权')
|
||
})
|
||
|
||
it('外链菜单:没有可授权模块,授权列表换成中间一句说明,入口勾选框独立可用', async () => {
|
||
const wrapper = await mountWithMenu('portal')
|
||
|
||
expect(moduleNames(wrapper)).toEqual([])
|
||
expect(wrapper.findAll('.power-row')).toHaveLength(0)
|
||
expect(wrapper.find('.power-grid').exists()).toBe(false)
|
||
// 没有模块可配:进度不渲染(没有分母),内容区给一句说明而不是一片空白
|
||
expect(wrapper.find('.power-head__meta').exists()).toBe(false)
|
||
expect(wrapper.find('.power-panel__state').text()).toBe('本页没有可配置的模块')
|
||
|
||
await setCheck(entryCheck(wrapper))
|
||
expect(lastEmitted(wrapper)).toEqual(['menu.portal'])
|
||
})
|
||
|
||
it('页面没绑模块(自定义页):只剩入口勾选框,勾选即全部权限', async () => {
|
||
const wrapper = await mountWithMenu('partner', { menuModules: {} })
|
||
|
||
expect(wrapper.find('.power-grid').exists()).toBe(false)
|
||
expect(wrapper.find('.power-panel__state').text()).toBe('本页没有可配置的模块')
|
||
expect(useChecks(wrapper)).toHaveLength(1)
|
||
|
||
await setCheck(entryCheck(wrapper))
|
||
expect(lastEmitted(wrapper)).toEqual(['menu.partner'])
|
||
})
|
||
|
||
it('未配置权限点的模块不渲染,也不为它单独出提示', async () => {
|
||
const wrapper = await mountWithMenu('user')
|
||
|
||
// 「币种」绑定了但没配权限点:不渲染成行,也不出任何提示
|
||
expect(moduleNames(wrapper)).toEqual(['用户管理'])
|
||
// 只有模块使用权限点的模块没有动作勾选框
|
||
expect(wrapper.findAll('.power-row__actions .checkbox')).toHaveLength(0)
|
||
expect(wrapper.find('.power-panel__main').text()).not.toContain('未配置权限点')
|
||
// 本页仍有可配置模块 → 内容区不出现整体的空态说明
|
||
expect(wrapper.find('.power-panel__state').exists()).toBe(false)
|
||
})
|
||
})
|
||
|
||
describe('功能权限面板:三层联动', () => {
|
||
it('勾模块级勾选框:只给该模块的模块使用权限,并补上菜单入口', async () => {
|
||
const wrapper = await mountWithMenu('partner')
|
||
|
||
await setCheck(moduleCheck(wrapper))
|
||
await nextTick()
|
||
|
||
// 勾上级不替下级勾选:只给「模块使用」,下面的操作要自己勾
|
||
expect(lastEmitted(wrapper)).toEqual(['module.v_b_othercompany', 'menu.partner'])
|
||
})
|
||
|
||
it('已授权的模块点勾选框:收回该模块全部权限', async () => {
|
||
const wrapper = await mountWithMenu('partner', {
|
||
modelValue: ['module.v_b_othercompany', 'action.v_b_othercompany.create'],
|
||
})
|
||
|
||
// 勾选框显示的是"这个数据给没给"(已给 → 勾选),点一下就是它的反面:整块收回
|
||
await setCheck(moduleCheck(wrapper), false)
|
||
await nextTick()
|
||
|
||
expect(lastEmitted(wrapper)).toEqual([])
|
||
})
|
||
|
||
it('取消模块级勾选框:收回该模块的全部权限点,不动菜单入口', async () => {
|
||
const wrapper = await mountWithMenu('partner', {
|
||
modelValue: ['menu.partner', ...OTHERCOMPANY_ALL],
|
||
})
|
||
|
||
await setCheck(moduleCheck(wrapper), false)
|
||
await nextTick()
|
||
|
||
expect(lastEmitted(wrapper)).toEqual(['menu.partner'])
|
||
})
|
||
|
||
it('勾单个动作:自动补上模块使用与菜单入口', async () => {
|
||
const wrapper = await mountWithMenu('partner')
|
||
|
||
await setCheck(actionCheck(wrapper, '合作伙伴', '新增'))
|
||
await nextTick()
|
||
|
||
expect(lastEmitted(wrapper)).toEqual([
|
||
'action.v_b_othercompany.create',
|
||
'module.v_b_othercompany',
|
||
'menu.partner',
|
||
])
|
||
})
|
||
|
||
it('取消单个动作:不动模块使用与入口,模块仍显示为已授权', async () => {
|
||
const wrapper = await mountWithMenu('partner', {
|
||
modelValue: ['menu.partner', 'module.v_b_othercompany', 'action.v_b_othercompany.create'],
|
||
})
|
||
|
||
await setCheck(actionCheck(wrapper, '合作伙伴', '新增'), false)
|
||
await nextTick()
|
||
|
||
expect(lastEmitted(wrapper)).toEqual(['menu.partner', 'module.v_b_othercompany'])
|
||
// 这个数据本身还要用(模块使用还在)→ 勾选框保持勾选;撤了几项动作看块头计数
|
||
expect(moduleCheck(wrapper, 0).classes()).toContain('checkbox-checked')
|
||
expect(rowCount(wrapper, 0).text()).toBe('已选 0/3')
|
||
})
|
||
|
||
it('开菜单入口:只给入口本身,本页模块要自己勾', async () => {
|
||
const wrapper = await mountWithMenu('partner')
|
||
|
||
await setCheck(entryCheck(wrapper))
|
||
await nextTick()
|
||
|
||
// 勾上级不替下级勾选:打开入口只给入口,模块与操作留给下面自己勾
|
||
expect(lastEmitted(wrapper)).toEqual(['menu.partner'])
|
||
})
|
||
|
||
it('关菜单入口:连带收回该菜单下的全部模块与动作授权', async () => {
|
||
const wrapper = await mountWithMenu('partner', {
|
||
modelValue: ['menu.partner', 'module.v_b_othercompany', 'action.v_b_othercompany.create'],
|
||
})
|
||
|
||
await setCheck(entryCheck(wrapper), false)
|
||
await nextTick()
|
||
|
||
expect(lastEmitted(wrapper)).toEqual([])
|
||
})
|
||
|
||
it('勾选态回显:各行动作各管各的,只勾动作时模块级也是已勾选', async () => {
|
||
const wrapper = await mountWithMenu('partner', {
|
||
modelValue: ['action.v_b_othercompany.create'],
|
||
})
|
||
|
||
expect(actionCheck(wrapper, '合作伙伴', '新增').classes()).toContain('checkbox-checked')
|
||
expect(actionCheck(wrapper, '合作伙伴', '修改').classes()).not.toContain('checkbox-checked')
|
||
// 同名动作在别的模块行里不受影响
|
||
expect(actionCheck(wrapper, '联系人', '新增').classes()).not.toContain('checkbox-checked')
|
||
// 操作有权限 → 数据也有权限:模块级勾选框是勾选的(没有半选态)
|
||
expect(moduleCheck(wrapper, 0).classes()).toContain('checkbox-checked')
|
||
expect(moduleCheck(wrapper, 0).classes()).not.toContain('checkbox-indeterminate')
|
||
})
|
||
})
|