diff --git a/web/app/components/header/account-setting/permissions-page/role-list/__tests__/row-menu.spec.tsx b/web/app/components/header/account-setting/permissions-page/role-list/__tests__/row-menu.spec.tsx new file mode 100644 index 0000000000..5620d16032 --- /dev/null +++ b/web/app/components/header/account-setting/permissions-page/role-list/__tests__/row-menu.spec.tsx @@ -0,0 +1,130 @@ +import type { Role } from '@/models/access-control' +import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import RowMenu from '../row-menu' + +const mockWorkspacePermissionKeys = vi.hoisted(() => ({ + value: ['workspace.role.manage'] as string[], +})) + +const mockCopyRole = vi.hoisted(() => vi.fn()) +const mockDeleteRole = vi.hoisted(() => vi.fn()) + +vi.mock('@/context/app-context', () => ({ + useSelector: (selector: (state: { workspacePermissionKeys: string[] }) => T): T => selector({ + workspacePermissionKeys: mockWorkspacePermissionKeys.value, + }), +})) + +vi.mock('@/service/access-control/use-workspace-roles', () => ({ + useCopyWorkspaceRole: () => ({ + mutateAsync: mockCopyRole, + }), + useDeleteWorkspaceRole: () => ({ + mutateAsync: mockDeleteRole, + isPending: false, + }), +})) + +vi.mock('@langgenius/dify-ui/toast', () => ({ + toast: { + success: vi.fn(), + }, +})) + +const createRole = (overrides: Partial = {}): Role => ({ + id: 'role-1', + tenant_id: 'tenant-1', + type: 'workspace', + category: 'global_system_default', + name: 'Owner', + description: 'Workspace owner', + is_builtin: true, + permission_keys: [], + role_tag: '', + ...overrides, +}) + +const openMenu = async () => { + const user = userEvent.setup() + await user.click(screen.getByRole('button', { name: 'common.operation.moreActions' })) + return user +} + +describe('RowMenu', () => { + beforeEach(() => { + vi.clearAllMocks() + mockWorkspacePermissionKeys.value = ['workspace.role.manage'] + }) + + describe('Rendering', () => { + it('should render view action for the owner system role', async () => { + render( + , + ) + + await openMenu() + + expect(screen.getByRole('menuitem', { name: 'common.operation.view' })).toBeInTheDocument() + expect(screen.queryByRole('menuitem', { name: 'common.operation.edit' })).not.toBeInTheDocument() + }) + + it('should hide view action for non-owner system roles', async () => { + render( + , + ) + + await openMenu() + + expect(screen.queryByRole('menuitem', { name: 'common.operation.view' })).not.toBeInTheDocument() + expect(screen.getByRole('menuitem', { name: 'common.operation.edit' })).toBeInTheDocument() + }) + + it('should hide view action for custom roles', async () => { + render( + , + ) + + await openMenu() + + expect(screen.queryByRole('menuitem', { name: 'common.operation.view' })).not.toBeInTheDocument() + expect(screen.getByRole('menuitem', { name: 'common.operation.edit' })).toBeInTheDocument() + expect(screen.getByRole('menuitem', { name: 'common.operation.duplicate' })).toBeInTheDocument() + expect(screen.getByRole('menuitem', { name: 'common.operation.delete' })).toBeInTheDocument() + }) + }) + + describe('User Interactions', () => { + it('should call onView when clicking the owner view action', async () => { + const onView = vi.fn() + const role = createRole({ role_tag: 'owner' }) + render( + , + ) + + const user = await openMenu() + await user.click(screen.getByRole('menuitem', { name: 'common.operation.view' })) + + expect(onView).toHaveBeenCalledTimes(1) + expect(onView).toHaveBeenCalledWith(role) + }) + }) +}) diff --git a/web/app/components/header/account-setting/permissions-page/role-list/row-menu.tsx b/web/app/components/header/account-setting/permissions-page/role-list/row-menu.tsx index 8105be2c52..605937e81d 100644 --- a/web/app/components/header/account-setting/permissions-page/role-list/row-menu.tsx +++ b/web/app/components/header/account-setting/permissions-page/role-list/row-menu.tsx @@ -76,6 +76,7 @@ const RowMenu = ({ const canManageRoles = hasPermission(workspacePermissionKeys, 'workspace.role.manage') + const hasViewAction = roleCategory === 'global_system_default' && role.role_tag === 'owner' const hasEditAction = (roleCategory === 'global_custom' || (roleCategory === 'global_system_default' && role.role_tag !== 'owner')) && canManageRoles const hasDuplicateAction = roleCategory === 'global_custom' && canManageRoles const hasDeleteAction = roleCategory === 'global_custom' && canManageRoles @@ -87,9 +88,13 @@ const RowMenu = ({ - - {t('operation.view', { ns: 'common' })} - + { + hasViewAction && ( + + {t('operation.view', { ns: 'common' })} + + ) + } { hasEditAction && (