import ElementPlus, { type FormInstance } from 'element-plus' import { createPinia, setActivePinia } from 'pinia' import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils' import { afterEach, describe, expect, it, vi } from 'vitest' import { useAuthStore } from '@/core/auth/auth' import AppDialog from '@/shared/components/AppDialog.vue' import { createPlatformPermission, listPlatformPermissions, updatePlatformPermission } from '../api/platformIam' import PermissionsView from './PermissionsView.vue' vi.mock('../api/platformIam', () => ({ listPlatformPermissions: vi.fn(), createPlatformPermission: vi.fn(), updatePlatformPermission: vi.fn(), changePlatformPermissionStatus: vi.fn(), deletePlatformPermission: vi.fn(), })) enableAutoUnmount(afterEach) afterEach(() => { document.body.innerHTML = ''; vi.unstubAllGlobals(); vi.clearAllMocks() }) describe('permission editor modal', () => { it.each(['create', 'edit'])('keeps the %s form in the centered dialog and preserves failed writes', async mode => { vi.stubGlobal('ResizeObserver', class { observe() {} unobserve() {} disconnect() {} }) const pinia = createPinia() setActivePinia(pinia) const auth = useAuthStore() auth.isServerSession = true auth.permissions = ['platform:permission:list', 'platform:permission:create', 'platform:permission:edit'] vi.mocked(listPlatformPermissions).mockResolvedValue([{ id: '90071992547409931', tenantId: 0, appCode: 'PLATFORM', permissionType: 'MENU', parentId: 0, permissionCode: 'platform:sample:list', permissionName: '测试菜单', routePath: '/sample', httpMethod: null, sortNo: 10, status: 'ACTIVE', }]) vi.mocked(createPlatformPermission).mockRejectedValue(new Error('保存失败')) vi.mocked(updatePlatformPermission).mockRejectedValue(new Error('保存失败')) const wrapper = mount(PermissionsView, { attachTo: document.body, global: { plugins: [pinia, ElementPlus] } }) await flushPromises() await wrapper.findAll(mode === 'create' ? 'button' : '.el-table__body-wrapper button').find(button => button.text().trim() === (mode === 'create' ? '新增权限' : '编辑'))!.trigger('click') await flushPromises() const dialog = document.querySelector('.el-dialog.permission-editor-dialog.admin-density-dialog')! expect(dialog).not.toBeNull() expect(dialog.style.getPropertyValue('--el-dialog-width')).toBe('min(720px, calc(100vw - 28px))') expect(document.querySelector('.el-drawer')).toBeNull() expect(dialog.querySelectorAll('.el-form-item')).toHaveLength(6) const codeInput = dialog.querySelector('input[placeholder="如:platform:customer:list"]') if (mode === 'edit') { expect(codeInput).toBeNull(); expect(dialog.textContent).toContain('platform:sample:list') } else expect(codeInput?.disabled).toBe(false) // Vitest externalizes Element Plus: its CJS validator interop differs from the Vite browser bundle. // Exercise the dialog's validation gate explicitly, without changing production rules. const form = wrapper.findComponent({ name: 'ElForm' }) expect(form.props('rules').name).toEqual(expect.arrayContaining([expect.objectContaining({ required: true })])) const validate = vi.spyOn(form.vm.$.exposed as FormInstance, 'validate').mockResolvedValue(true) if (mode === 'create') { validate.mockRejectedValueOnce({ name: [{ message: '请输入权限名称' }] }) wrapper.getComponent(AppDialog).vm.$emit('confirm') await flushPromises() expect(createPlatformPermission).not.toHaveBeenCalled() for (const [placeholder, value] of [['用户可理解的中文名称', '新增菜单'], ['如:platform:customer:list', 'platform:new:list'], ['如:/customers', '/new']]) { const input = dialog.querySelector(`input[placeholder="${placeholder}"]`)! input.value = value! input.dispatchEvent(new Event('input', { bubbles: true })) } } await flushPromises() wrapper.getComponent(AppDialog).vm.$emit('confirm') await flushPromises() if (mode === 'create') expect(createPlatformPermission).toHaveBeenCalledWith({ type: 'MENU', code: 'platform:new:list', name: '新增菜单', parentId: '0', routePath: '/new', httpMethod: null, sortNo: 10 }) else expect(updatePlatformPermission).toHaveBeenCalledWith('90071992547409931', { name: '测试菜单', parentId: null, routePath: '/sample', httpMethod: null, sortNo: 10 }) expect(wrapper.getComponent(AppDialog).props('modelValue')).toBe(true) if (mode === 'create') expect(codeInput?.value).toBe('platform:new:list') else expect(dialog.textContent).toContain('platform:sample:list') }) })