import { createPinia, setActivePinia } from 'pinia'
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { flushPromises, shallowMount } from '@vue/test-utils'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { listPlatformPermissions, type PlatformPermissionResource } from '@/modules/iam/api/platformIam'
import { useAuthStore } from '@/core/auth/auth'
import PermissionsView from './PermissionsView.vue'
const permissionsSource = readFileSync(resolve(process.cwd(), 'src/modules/iam/pages/PermissionsView.vue'), 'utf8')
vi.mock('@/modules/iam/api/platformIam', () => ({
changePlatformPermissionStatus: vi.fn(),
createPlatformPermission: vi.fn(),
deletePlatformPermission: vi.fn(),
listPlatformPermissions: vi.fn(),
updatePlatformPermission: vi.fn(),
}))
const permission: PlatformPermissionResource = {
id: 5100010,
tenantId: 0,
appCode: 'PLATFORM',
permissionType: 'MENU',
parentId: 0,
permissionCode: 'platform:customer:list',
permissionName: '企业客户',
routePath: '/customers',
httpMethod: null,
sortNo: 10,
status: 'ACTIVE',
}
const allActionPermissions = [
'platform:permission:list',
'platform:permission:create',
'platform:permission:edit',
'platform:permission:status',
'platform:permission:delete',
]
function mountServerView(permissionCodes = ['platform:permission:list', 'platform:permission:create']) {
const pinia = createPinia()
setActivePinia(pinia)
const auth = useAuthStore()
auth.isServerSession = true
auth.permissions = permissionCodes
return shallowMount(PermissionsView, {
global: {
plugins: [pinia],
directives: { loading: () => undefined },
stubs: {
RowActions: false,
AdminPage: { template: '' },
ListPageLayout: { template: '' },
FormSection: { template: '' },
AppDialog: { template: '' },
LoadErrorState: { props: ['message'], template: '
{{ message }}
' },
DataTable: {
props: ['data'],
template: '',
},
'el-alert': { props: ['title'], template: '{{ title }}
' },
'el-input': true,
'el-option': true,
'el-select': true,
'el-button': { props: ['disabled'], template: '' },
'el-dropdown': { template: '
' },
'el-dropdown-menu': { template: '
' },
'el-dropdown-item': { props: ['disabled'], template: '' },
'el-table': true,
'el-table-column': true,
'el-input-number': true,
'el-radio-group': true,
'el-radio-button': true,
'el-drawer': { template: '' },
'el-form': { template: '' },
'el-form-item': { template: '' },
},
},
})
}
describe('PermissionsView IAM mode', () => {
it('keeps the shared status badge prop defined during table slot initialization', () => {
expect(permissionsSource).toContain("(row as Permission).status || '—'")
})
it('uses outlined business action buttons without global tree view helpers', () => {
expect(permissionsSource).toContain("import RowActions, { type RowActionItem } from '@/shared/components/RowActions.vue'")
expect(permissionsSource).toContain("{ key: 'add-child', label: '添加下级', priority: 400")
expect(permissionsSource).toContain("{ key: 'edit', label: '编辑', priority: 300")
expect(permissionsSource).toContain("{ key: 'status', label: row.status === '启用' ? '停用' : '启用'")
expect(permissionsSource).toContain("{ key: 'delete', label: '删除', tone: 'danger', divided: true")
expect(permissionsSource).toContain(' {
vi.mocked(listPlatformPermissions).mockReset()
})
it('keeps technical permission fields out of the default list and labels the table', () => {
expect(permissionsSource).not.toContain("key: 'code', label: '权限编码'")
expect(permissionsSource).not.toContain(' {
vi.mocked(listPlatformPermissions).mockResolvedValue([permission])
const wrapper = mountServerView()
await flushPromises()
expect(listPlatformPermissions).toHaveBeenCalledTimes(1)
expect(wrapper.text()).not.toContain('权限资源树')
})
it('shows a recoverable state when the permission list fails', async () => {
vi.mocked(listPlatformPermissions).mockRejectedValue(new Error('IAM unavailable'))
const wrapper = mountServerView()
await flushPromises()
const alertText = wrapper.findAll('[role="alert"]').map((item) => item.text()).join(' ')
expect(alertText).toContain('权限资源加载失败,请稍后重试')
expect(alertText).not.toContain('IAM unavailable')
})
it('shows only add-child directly for a built-in menu even with all permissions', async () => {
vi.mocked(listPlatformPermissions).mockResolvedValue([{ ...permission, builtIn: true }])
const wrapper = mountServerView(allActionPermissions)
await flushPromises()
const actions = wrapper.get('[data-row-id="5100010"]')
expect(actions.text()).toContain('添加下级')
expect(actions.text()).not.toContain('系统内置')
expect(actions.text()).not.toContain('编辑')
expect(actions.text()).not.toContain('停用')
expect(actions.text()).not.toContain('删除')
expect(actions.find('.dropdown').exists()).toBe(false)
})
it('keeps three frequent actions direct and moves delete into more for a custom menu', async () => {
vi.mocked(listPlatformPermissions).mockResolvedValue([{ ...permission, builtIn: false }])
const wrapper = mountServerView(allActionPermissions)
await flushPromises()
const actions = wrapper.get('[data-row-id="5100010"]')
expect(actions.text()).toContain('添加下级')
expect(actions.text()).toContain('编辑')
expect(actions.text()).toContain('停用')
expect(actions.get('.dropdown').text()).toContain('删除')
expect(actions.get('.dropdown-item').classes()).toContain('danger-menu-item')
expect(actions.get('[aria-label="企业客户的更多操作"]').attributes('title')).toBe('企业客户的更多操作')
})
it('shows edit, status, and delete directly for a custom non-menu resource', async () => {
vi.mocked(listPlatformPermissions).mockResolvedValue([{
...permission,
permissionType: 'BUTTON',
permissionCode: 'platform:customer:edit',
permissionName: '编辑企业客户',
routePath: null,
builtIn: false,
}])
const wrapper = mountServerView(allActionPermissions)
await flushPromises()
const actions = wrapper.get('[data-row-id="5100010"]')
expect(actions.text()).not.toContain('添加下级')
expect(actions.text()).toContain('编辑')
expect(actions.text()).toContain('停用')
expect(actions.text()).toContain('删除')
expect(actions.find('.dropdown').exists()).toBe(false)
})
it('shows all three actually available menu actions directly when permissions are reduced', async () => {
vi.mocked(listPlatformPermissions).mockResolvedValue([{ ...permission, builtIn: false }])
const wrapper = mountServerView([
'platform:permission:list',
'platform:permission:create',
'platform:permission:edit',
'platform:permission:delete',
])
await flushPromises()
const actions = wrapper.get('[data-row-id="5100010"]')
expect(actions.text()).toContain('添加下级')
expect(actions.text()).toContain('编辑')
expect(actions.text()).toContain('删除')
expect(actions.text()).not.toContain('停用')
expect(actions.find('.dropdown').exists()).toBe(false)
})
it('falls back from a missing name and code to the row id for the more-action label', async () => {
vi.mocked(listPlatformPermissions).mockResolvedValue([{
...permission,
permissionName: '',
permissionCode: '',
builtIn: false,
}])
const wrapper = mountServerView(allActionPermissions)
await flushPromises()
const moreButton = wrapper.get('[data-row-id="5100010"] [aria-label="当前权限的更多操作"]')
expect(moreButton.attributes('title')).toBe('当前权限的更多操作')
expect(moreButton.attributes('aria-label')).not.toMatch(/undefined|null/)
})
it('gives more-action buttons distinct accessible names across rows', async () => {
vi.mocked(listPlatformPermissions).mockResolvedValue([
{ ...permission, builtIn: false },
{
...permission,
id: 5100020,
permissionCode: 'platform:role:list',
permissionName: '角色管理',
routePath: '/roles',
builtIn: false,
},
])
const wrapper = mountServerView(allActionPermissions)
await flushPromises()
const labels = wrapper.findAll('[aria-label$="的更多操作"]').map((button) => button.attributes('aria-label'))
expect(labels).toEqual(['企业客户的更多操作', '角色管理的更多操作'])
expect(new Set(labels).size).toBe(labels.length)
})
})