Files
Agent-System-Frontend/src/components/TaskSidebar.test.tsx
T
weichen e73f2ad9dd
governance / governance (pull_request) Successful in 13s
ci / test-and-build (pull_request) Successful in 3m38s
U5-Frontend integration-tests via PI direct (cursor 3/3 blocked on Playwright + vitest-only)
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.
2026-10-20 06:50:53 +00:00

72 lines
3.5 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import { renderToStaticMarkup } from 'react-dom/server'
import { TaskSidebar } from './TaskSidebar'
const mocked = vi.hoisted(() => ({ state: {} as Record<string, unknown> }))
vi.mock('../store', () => ({
useStore: (select: (value: Record<string, unknown>) => unknown) => select(mocked.state),
store: { openInbox: vi.fn(), openFiles: vi.fn(), focusPending: vi.fn(), setUI: vi.fn(), toggleLeft: vi.fn() },
}))
const member = { owner: 'bob', name: 'Bob', auth: true, systemActions: [],
linkedCapabilities: { projectCreate: false, linkedActions: [], authorityEpoch: 1 } }
const creator = { ...member, linkedCapabilities: { projectCreate: true, source: 'role', linkedActions: [], authorityEpoch: 1 } }
function render(patch: Record<string, unknown>) {
mocked.state = {
projects: [], projectsLoaded: true, projectSync: { status: 'ok', failures: 0 }, tasks: [], me: member,
adminSections: [], artifacts: [], cards: [], projectCards: [], serverWorkspaces: [],
rdmAuditAvailable: false, rdmCockpitAvailable: false, currentConvId: null, currentProjectId: null,
ui: { theme: 'light', showArchived: false },
...patch,
}
return renderToStaticMarkup(<TaskSidebar />)
}
beforeEach(() => { mocked.state = {} })
describe('EmptyListNoCreate sidebar', () => {
it('shows a waiting-for-invite state without inviting a same-name project for a member without creation', () => {
const html = render({})
expect(html).toContain('尚无项目:等待项目负责人邀请你加入,加入后会自动出现在这里。')
expect(html).not.toContain('新建项目')
expect(html).toMatch(/<button class="tsb-newtask-main" disabled=""/)
})
it('shows a loading state instead of an empty state before the list arrives', () => {
const html = render({ projectsLoaded: false })
expect(html).toContain('正在加载项目列表…')
expect(html).not.toContain('等待项目负责人邀请')
})
it('points a creator to explicit project creation', () => {
expect(render({ me: creator })).toContain('还没有项目:从「新建项目」开始。')
})
})
describe('AutoDiscover sidebar status', () => {
it('surfaces a degraded or offline project sync instead of silently showing stale projects', () => {
expect(render({ projectSync: { status: 'degraded', failures: 2 } })).toContain('项目列表同步异常,正在自动重试')
expect(render({ projectSync: { status: 'offline', failures: 0 } })).toContain('网络已断开,恢复后自动同步项目列表')
expect(render({})).not.toContain('同步异常')
})
it('keeps conversation creation available once a discovered project is current', () => {
const html = render({ projects: [{ id: 'host-b', name: '项目P', owner: 'carol' }], currentProjectId: 'host-b' })
expect(html).toMatch(/<button class="tsb-newtask-main" title="在当前项目新建对话/)
expect(html).toContain('从「新建对话」开始')
})
it('falls back to a project-less sidebar when the current discovery has no project', () => {
const html = render({ projects: [], currentProjectId: null })
expect(html).toContain('尚无项目')
expect(html).not.toContain('同步异常')
})
it('does not render a sync notice when the listing succeeded', () => {
expect(render({ projectSync: { status: 'ok', failures: 0 } })).not.toContain('同步异常')
expect(render({ projectSync: { status: 'idle', failures: 0 } })).not.toContain('同步异常')
})
})