Why: cursor 3/3 exhausted; PI direct dispatched as host integration work. Scope: 8 test files (7 modified + 1 new), 255 insertions, 29 new vitest tests covering 11 U4 frontend-auto-discover scenarios end-to-end: - AutoDiscover (3 new in store.test.ts) - CapabilityGroup (3 new in rolePermissions.test.ts + 6 new in panels) - CreatorOwnerMemberDuties (4 new across adminRdmSections/projectmeta/AccountUsersPanel) - MemberManagementEntryHidden (3 new in adminRdmSections/projectmeta) - EmptyListNoCreate (4 new in projectmeta/TaskSidebar) - DefaultProjectDisabled (1 new in store.test.ts/projectmeta) - RefreshBackoff + RefreshSingleFlight + RefreshHiddenPause + RefreshReconnectCompensate - ProjectLifecycleConversation (5 new in test/conversation-work.test.ts) Verification: npm run build ok (5.35s); ./node_modules/.bin/vitest run 656/656 (was 627, +29 new); new 29 tests across all 11 patterns all pass; no production files touched. Refs: plan revision 3 U5-Frontend; /tmp/iam-link-u5-frontend-pi-verify-20261019.json exit=0.
73 lines
4.5 KiB
TypeScript
73 lines
4.5 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest'
|
|
import { renderToStaticMarkup } from 'react-dom/server'
|
|
import type { PermissionDescriptor } from '../api'
|
|
import { GrantChangePreview, GrantGroupEditor, ProjectCreateSourceNote, toggleGrantGroup } from './AccountUsersPanel'
|
|
import { buildCapabilityGroups } from '../rolePermissions'
|
|
|
|
const descriptor = (actionId: string, label: string, group: string, scope: 'system' | 'project' = 'system'): PermissionDescriptor =>
|
|
({ actionId, label, description: '', group, scope, protected: scope === 'system' && actionId !== 'project.create' })
|
|
const catalog = [
|
|
descriptor('system.users.view', '查看用户', '用户管理'),
|
|
descriptor('system.users.create', '创建用户', '用户管理'),
|
|
descriptor('system.roles.view', '查看角色', '角色管理'),
|
|
descriptor('project.create', '创建项目', '项目入口'),
|
|
descriptor('project.members.manage', '管理项目成员', '项目管理', 'project'),
|
|
]
|
|
const labels = Object.fromEntries(catalog.map(item => [item.actionId, item.label]))
|
|
|
|
describe('CapabilityGroup explicit grants', () => {
|
|
it('grants system capabilities by group instead of one checkbox per action', () => {
|
|
const html = renderToStaticMarkup(<GrantGroupEditor catalog={catalog} grants={['system.users.view']} disabled={false} onChange={vi.fn()} />)
|
|
expect(html.match(/type="checkbox"/g)).toHaveLength(3)
|
|
expect(html).toContain('授予 用户管理')
|
|
expect(html).toContain('用户管理(部分)')
|
|
expect(html).toContain('联动本人创建项目的设置、成员、项目权限与文档职责管理')
|
|
expect(html).not.toContain('管理项目成员')
|
|
})
|
|
|
|
it('toggles every action of a group together', () => {
|
|
const users = buildCapabilityGroups(catalog.filter(item => item.scope === 'system')).find(group => group.id === 'catalog:用户管理')!
|
|
expect(toggleGrantGroup(['system.roles.view'], users, true)).toEqual(['system.roles.view', 'system.users.create', 'system.users.view'])
|
|
expect(toggleGrantGroup(['system.users.view', 'system.roles.view'], users, false)).toEqual(['system.roles.view'])
|
|
})
|
|
|
|
it('previews the grant difference with its source and the single affected user', () => {
|
|
const html = renderToStaticMarkup(<GrantChangePreview baseline={['system.users.view']} grants={['project.create']} labels={labels} owner="bob" />)
|
|
expect(html).toContain('影响来源:明确系统授权 · 影响 1 名用户(bob)')
|
|
expect(html).toContain('新增:创建项目(联动本人创建项目的管理资格)')
|
|
expect(html).toContain('撤销:查看用户')
|
|
const revoke = renderToStaticMarkup(<GrantChangePreview baseline={['project.create']} grants={[]} labels={labels} owner="bob" />)
|
|
expect(revoke).toContain('已创建项目的管理资格不被反向删除')
|
|
})
|
|
})
|
|
|
|
describe('CreatorOwnerMemberDuties capability source', () => {
|
|
it('shows where an account gets project creation and what it links to', () => {
|
|
const role = { name: '项目经理', permissions: [{ actionId: 'project.create', allowed: true }] }
|
|
expect(renderToStaticMarkup(<ProjectCreateSourceNote role={role} grants={['project.create']} />))
|
|
.toContain('创建项目来源:职位角色「项目经理」 + 明确系统授权')
|
|
expect(renderToStaticMarkup(<ProjectCreateSourceNote role={null} grants={[]} />))
|
|
.toContain('被加入项目后由成员关系提供基础协作')
|
|
})
|
|
|
|
it('lists each project creation source separately when only one is present', () => {
|
|
expect(renderToStaticMarkup(<ProjectCreateSourceNote role={{ name: '项目经理', permissions: [{ actionId: 'project.create', allowed: true }] }} grants={[]} />))
|
|
.toContain('创建项目来源:职位角色「项目经理」')
|
|
expect(renderToStaticMarkup(<ProjectCreateSourceNote role={null} grants={['project.create']} />))
|
|
.toContain('创建项目来源:明确系统授权')
|
|
})
|
|
})
|
|
|
|
describe('CapabilityGroup empty preview', () => {
|
|
it('renders a no-change preview when baseline equals grants', () => {
|
|
const html = renderToStaticMarkup(<GrantChangePreview baseline={['system.users.view']} grants={['system.users.view']} labels={labels} owner="bob" />)
|
|
expect(html).toContain('系统授权没有变化')
|
|
})
|
|
|
|
it('still names the affected user when nothing visible changes', () => {
|
|
const html = renderToStaticMarkup(<GrantChangePreview baseline={[]} grants={[]} labels={labels} owner="bob" />)
|
|
expect(html).toContain('系统授权没有变化')
|
|
expect(html).not.toContain('影响来源')
|
|
})
|
|
})
|