|
@@ -60,7 +60,7 @@ describe('workspace browser rows', () => {
|
|
|
it('omits only an empty leading status slot in the hierarchy-free flat list', () => {
|
|
it('omits only an empty leading status slot in the hierarchy-free flat list', () => {
|
|
|
const idle: SessionNode = {
|
|
const idle: SessionNode = {
|
|
|
id: sid('flat'), title: 'Flat Session', blank: false, running: false,
|
|
id: sid('flat'), title: 'Flat Session', blank: false, running: false,
|
|
|
- runningSubagentCount: 0, completed: false, updatedAt: 0,
|
|
|
|
|
|
|
+ runningSubagentCount: 0, completed: false, hasActiveSchedule: false, updatedAt: 0,
|
|
|
}
|
|
}
|
|
|
const view = render(<SessionNodeItem node={idle} currentId={undefined} now={0} onOpen={vi.fn()}
|
|
const view = render(<SessionNodeItem node={idle} currentId={undefined} now={0} onOpen={vi.fn()}
|
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} flat t={t} />)
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} flat t={t} />)
|
|
@@ -81,6 +81,7 @@ describe('workspace browser rows', () => {
|
|
|
running: true,
|
|
running: true,
|
|
|
runningSubagentCount: 0,
|
|
runningSubagentCount: 0,
|
|
|
completed: false,
|
|
completed: false,
|
|
|
|
|
+ hasActiveSchedule: false,
|
|
|
snippet: 'matching message excerpt',
|
|
snippet: 'matching message excerpt',
|
|
|
}
|
|
}
|
|
|
render(<SearchResultItem result={result} currentId={result.id} onOpen={onOpen} t={t} />)
|
|
render(<SearchResultItem result={result} currentId={result.id} onOpen={onOpen} t={t} />)
|
|
@@ -95,6 +96,26 @@ describe('workspace browser rows', () => {
|
|
|
expect(onOpen).toHaveBeenCalledWith(result.id)
|
|
expect(onOpen).toHaveBeenCalledWith(result.id)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ it('keeps the active-Schedule marker after a search title and inside the row action', () => {
|
|
|
|
|
+ const onOpen = vi.fn()
|
|
|
|
|
+ const result: SearchResultNode = {
|
|
|
|
|
+ id: sid('scheduled-result'), title: 'Scheduled result', workspace: 'Project',
|
|
|
|
|
+ running: false, runningSubagentCount: 0, completed: false, hasActiveSchedule: true,
|
|
|
|
|
+ }
|
|
|
|
|
+ render(<SearchResultItem result={result} currentId={undefined} onOpen={onOpen} t={t} />)
|
|
|
|
|
+
|
|
|
|
|
+ const row = screen.getByRole('treeitem')
|
|
|
|
|
+ const title = screen.getByText('Scheduled result')
|
|
|
|
|
+ const indicator = screen.getByRole('img', { name: '有活动定时任务' })
|
|
|
|
|
+ expect(title.nextElementSibling).toBe(indicator)
|
|
|
|
|
+ expect(indicator.getAttribute('title')).toBe('有活动定时任务')
|
|
|
|
|
+ expect(indicator.getAttribute('tabindex')).toBeNull()
|
|
|
|
|
+ expect(row.querySelectorAll('button')).toHaveLength(0)
|
|
|
|
|
+
|
|
|
|
|
+ fireEvent.click(indicator)
|
|
|
|
|
+ expect(onOpen).toHaveBeenCalledWith(result.id)
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
it.each([
|
|
it.each([
|
|
|
['approval', '等待审批'],
|
|
['approval', '等待审批'],
|
|
|
['plan-review', '计划待审'],
|
|
['plan-review', '计划待审'],
|
|
@@ -103,6 +124,7 @@ describe('workspace browser rows', () => {
|
|
|
const result: SearchResultNode = {
|
|
const result: SearchResultNode = {
|
|
|
id: sid(pendingInteraction), title: 'Needs input', workspace: 'Project',
|
|
id: sid(pendingInteraction), title: 'Needs input', workspace: 'Project',
|
|
|
pendingInteraction, running: true, runningSubagentCount: 0, completed: false,
|
|
pendingInteraction, running: true, runningSubagentCount: 0, completed: false,
|
|
|
|
|
+ hasActiveSchedule: false,
|
|
|
}
|
|
}
|
|
|
render(<SearchResultItem result={result} currentId={undefined} onOpen={vi.fn()} t={t} />)
|
|
render(<SearchResultItem result={result} currentId={undefined} onOpen={vi.fn()} t={t} />)
|
|
|
const row = screen.getByRole('treeitem')
|
|
const row = screen.getByRole('treeitem')
|
|
@@ -131,7 +153,7 @@ describe('workspace browser rows', () => {
|
|
|
it('renders and opens a selected running Session row', () => {
|
|
it('renders and opens a selected running Session row', () => {
|
|
|
const node: SessionNode = {
|
|
const node: SessionNode = {
|
|
|
id: sid('session'), title: 'Session', blank: false, running: true,
|
|
id: sid('session'), title: 'Session', blank: false, running: true,
|
|
|
- runningSubagentCount: 0, completed: false, updatedAt: 0,
|
|
|
|
|
|
|
+ runningSubagentCount: 0, completed: false, hasActiveSchedule: false, updatedAt: 0,
|
|
|
}
|
|
}
|
|
|
const onOpen = vi.fn()
|
|
const onOpen = vi.fn()
|
|
|
render(
|
|
render(
|
|
@@ -147,12 +169,44 @@ describe('workspace browser rows', () => {
|
|
|
expect(onOpen).toHaveBeenCalledWith(node.id)
|
|
expect(onOpen).toHaveBeenCalledWith(node.id)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ it('keeps the active-Schedule marker between the title and time in grouped and flat rows', () => {
|
|
|
|
|
+ const onOpen = vi.fn()
|
|
|
|
|
+ const node: SessionNode = {
|
|
|
|
|
+ id: sid('scheduled-session'), title: 'Scheduled Session', blank: false, running: false,
|
|
|
|
|
+ runningSubagentCount: 0, completed: false, hasActiveSchedule: true, updatedAt: 0,
|
|
|
|
|
+ }
|
|
|
|
|
+ const view = render(
|
|
|
|
|
+ <SessionNodeItem node={node} currentId={undefined} now={0} onOpen={onOpen}
|
|
|
|
|
+ onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />,
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ const assertIndicator = (): HTMLElement => {
|
|
|
|
|
+ const title = screen.getByText('Scheduled Session')
|
|
|
|
|
+ const time = screen.getByText('刚刚')
|
|
|
|
|
+ const indicator = screen.getByRole('img', { name: '有活动定时任务' })
|
|
|
|
|
+ expect(title.nextElementSibling).toBe(indicator)
|
|
|
|
|
+ expect(indicator.nextElementSibling).toBe(time)
|
|
|
|
|
+ expect(indicator.getAttribute('title')).toBe('有活动定时任务')
|
|
|
|
|
+ expect(indicator.getAttribute('tabindex')).toBeNull()
|
|
|
|
|
+ return indicator
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fireEvent.click(assertIndicator())
|
|
|
|
|
+ expect(onOpen).toHaveBeenCalledWith(node.id)
|
|
|
|
|
+
|
|
|
|
|
+ view.rerender(
|
|
|
|
|
+ <SessionNodeItem node={node} currentId={undefined} now={0} onOpen={onOpen}
|
|
|
|
|
+ onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} flat t={t} />,
|
|
|
|
|
+ )
|
|
|
|
|
+ assertIndicator()
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
it('shows the green done dot only on a finished, unviewed session (live activity wins the slot)', () => {
|
|
it('shows the green done dot only on a finished, unviewed session (live activity wins the slot)', () => {
|
|
|
const renderRow = (over: Partial<SessionNode>) => render(
|
|
const renderRow = (over: Partial<SessionNode>) => render(
|
|
|
<SessionNodeItem
|
|
<SessionNodeItem
|
|
|
node={{
|
|
node={{
|
|
|
id: sid('s1'), title: 'One', blank: false, running: false,
|
|
id: sid('s1'), title: 'One', blank: false, running: false,
|
|
|
- runningSubagentCount: 0, completed: false, updatedAt: 0, ...over,
|
|
|
|
|
|
|
+ runningSubagentCount: 0, completed: false, hasActiveSchedule: false, updatedAt: 0, ...over,
|
|
|
}}
|
|
}}
|
|
|
currentId={undefined} now={0} onOpen={vi.fn()}
|
|
currentId={undefined} now={0} onOpen={vi.fn()}
|
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t}
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t}
|
|
@@ -184,7 +238,7 @@ describe('workspace browser rows', () => {
|
|
|
try {
|
|
try {
|
|
|
const node: SessionNode = {
|
|
const node: SessionNode = {
|
|
|
id: sid('owner'), title: 'Delegating', blank: false, running: false,
|
|
id: sid('owner'), title: 'Delegating', blank: false, running: false,
|
|
|
- runningSubagentCount: 2, completed: false, updatedAt: 0,
|
|
|
|
|
|
|
+ runningSubagentCount: 2, completed: false, hasActiveSchedule: false, updatedAt: 0,
|
|
|
}
|
|
}
|
|
|
render(<SessionNodeItem node={node} currentId={undefined} now={0} onOpen={vi.fn()}
|
|
render(<SessionNodeItem node={node} currentId={undefined} now={0} onOpen={vi.fn()}
|
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
@@ -206,7 +260,7 @@ describe('workspace browser rows', () => {
|
|
|
try {
|
|
try {
|
|
|
const node: SessionNode = {
|
|
const node: SessionNode = {
|
|
|
id: sid('owner'), title: 'Delegating', blank: false, running: true,
|
|
id: sid('owner'), title: 'Delegating', blank: false, running: true,
|
|
|
- runningSubagentCount: 1, completed: false, updatedAt: 0,
|
|
|
|
|
|
|
+ runningSubagentCount: 1, completed: false, hasActiveSchedule: false, updatedAt: 0,
|
|
|
}
|
|
}
|
|
|
render(<SessionNodeItem node={node} currentId={undefined} now={0} onOpen={vi.fn()}
|
|
render(<SessionNodeItem node={node} currentId={undefined} now={0} onOpen={vi.fn()}
|
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
@@ -227,7 +281,7 @@ describe('workspace browser rows', () => {
|
|
|
it('keeps child activity as a secondary status while user attention is primary', () => {
|
|
it('keeps child activity as a secondary status while user attention is primary', () => {
|
|
|
const node: SessionNode = {
|
|
const node: SessionNode = {
|
|
|
id: sid('owner'), title: 'Needs input', blank: false, pendingInteraction: 'question',
|
|
id: sid('owner'), title: 'Needs input', blank: false, pendingInteraction: 'question',
|
|
|
- running: false, runningSubagentCount: 1, completed: false, updatedAt: 0,
|
|
|
|
|
|
|
+ running: false, runningSubagentCount: 1, completed: false, hasActiveSchedule: false, updatedAt: 0,
|
|
|
}
|
|
}
|
|
|
render(<SessionNodeItem node={node} currentId={undefined} now={0} onOpen={vi.fn()}
|
|
render(<SessionNodeItem node={node} currentId={undefined} now={0} onOpen={vi.fn()}
|
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
@@ -242,7 +296,7 @@ describe('workspace browser rows', () => {
|
|
|
render(<SearchResultItem
|
|
render(<SearchResultItem
|
|
|
result={{
|
|
result={{
|
|
|
id: sid('result'), title: 'Done', workspace: 'Workspace', running: false,
|
|
id: sid('result'), title: 'Done', workspace: 'Workspace', running: false,
|
|
|
- runningSubagentCount: 0, completed: true,
|
|
|
|
|
|
|
+ runningSubagentCount: 0, completed: true, hasActiveSchedule: false,
|
|
|
}}
|
|
}}
|
|
|
currentId={undefined} onOpen={vi.fn()} t={t}
|
|
currentId={undefined} onOpen={vi.fn()} t={t}
|
|
|
/>)
|
|
/>)
|
|
@@ -374,7 +428,7 @@ describe('workspace browser rows', () => {
|
|
|
try {
|
|
try {
|
|
|
const node: SessionNode = {
|
|
const node: SessionNode = {
|
|
|
id: sid('s-blank'), title: 'ignored', blank: true, running: false,
|
|
id: sid('s-blank'), title: 'ignored', blank: true, running: false,
|
|
|
- runningSubagentCount: 0, completed: false, updatedAt: 0,
|
|
|
|
|
|
|
+ runningSubagentCount: 0, completed: false, hasActiveSchedule: false, updatedAt: 0,
|
|
|
}
|
|
}
|
|
|
render(<SessionNodeItem node={node} currentId={node.id} now={0} onOpen={vi.fn()}
|
|
render(<SessionNodeItem node={node} currentId={node.id} now={0} onOpen={vi.fn()}
|
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
@@ -401,7 +455,7 @@ describe('workspace browser rows', () => {
|
|
|
const onArchive = vi.fn()
|
|
const onArchive = vi.fn()
|
|
|
const node: SessionNode = {
|
|
const node: SessionNode = {
|
|
|
id: sid('s1'), title: 'One', blank: false, running: false,
|
|
id: sid('s1'), title: 'One', blank: false, running: false,
|
|
|
- runningSubagentCount: 0, completed: false, updatedAt: 0,
|
|
|
|
|
|
|
+ runningSubagentCount: 0, completed: false, hasActiveSchedule: false, updatedAt: 0,
|
|
|
}
|
|
}
|
|
|
render(<SessionNodeItem node={node} currentId={undefined} now={0} onOpen={onOpen}
|
|
render(<SessionNodeItem node={node} currentId={undefined} now={0} onOpen={onOpen}
|
|
|
onRename={onRename} onFork={onFork} onArchive={onArchive} t={t} />)
|
|
onRename={onRename} onFork={onFork} onArchive={onArchive} t={t} />)
|
|
@@ -435,7 +489,7 @@ describe('workspace browser rows', () => {
|
|
|
try {
|
|
try {
|
|
|
const node: SessionNode = {
|
|
const node: SessionNode = {
|
|
|
id: sid('s1'), title: 'Hovered', blank: false, running: true,
|
|
id: sid('s1'), title: 'Hovered', blank: false, running: true,
|
|
|
- runningSubagentCount: 0, completed: false, updatedAt: 0,
|
|
|
|
|
|
|
+ runningSubagentCount: 0, completed: false, hasActiveSchedule: false, updatedAt: 0,
|
|
|
}
|
|
}
|
|
|
render(<SessionNodeItem node={node} currentId={undefined} now={60_000} onOpen={vi.fn()}
|
|
render(<SessionNodeItem node={node} currentId={undefined} now={60_000} onOpen={vi.fn()}
|
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
@@ -466,7 +520,8 @@ describe('workspace browser rows', () => {
|
|
|
try {
|
|
try {
|
|
|
const node: SessionNode = {
|
|
const node: SessionNode = {
|
|
|
id: sid(pendingInteraction), title: 'Needs input', blank: false,
|
|
id: sid(pendingInteraction), title: 'Needs input', blank: false,
|
|
|
- pendingInteraction, running: true, runningSubagentCount: 0, completed: false, updatedAt: 0,
|
|
|
|
|
|
|
+ pendingInteraction, running: true, runningSubagentCount: 0, completed: false,
|
|
|
|
|
+ hasActiveSchedule: false, updatedAt: 0,
|
|
|
}
|
|
}
|
|
|
const view = render(<SessionNodeItem node={node} currentId={undefined} now={0} onOpen={vi.fn()}
|
|
const view = render(<SessionNodeItem node={node} currentId={undefined} now={0} onOpen={vi.fn()}
|
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
@@ -493,7 +548,7 @@ describe('workspace browser rows', () => {
|
|
|
try {
|
|
try {
|
|
|
const node: SessionNode = {
|
|
const node: SessionNode = {
|
|
|
id: sid('s1'), title: 'Quiet', blank: false, running: false,
|
|
id: sid('s1'), title: 'Quiet', blank: false, running: false,
|
|
|
- runningSubagentCount: 0, completed: false, updatedAt: 0,
|
|
|
|
|
|
|
+ runningSubagentCount: 0, completed: false, hasActiveSchedule: false, updatedAt: 0,
|
|
|
}
|
|
}
|
|
|
render(<SessionNodeItem node={node} currentId={undefined} now={0} onOpen={vi.fn()}
|
|
render(<SessionNodeItem node={node} currentId={undefined} now={0} onOpen={vi.fn()}
|
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
@@ -511,7 +566,7 @@ describe('workspace browser rows', () => {
|
|
|
try {
|
|
try {
|
|
|
const node: SessionNode = {
|
|
const node: SessionNode = {
|
|
|
id: sid('s1'), title: 'Done', blank: false, running: false,
|
|
id: sid('s1'), title: 'Done', blank: false, running: false,
|
|
|
- runningSubagentCount: 0, completed: true, updatedAt: 0,
|
|
|
|
|
|
|
+ runningSubagentCount: 0, completed: true, hasActiveSchedule: false, updatedAt: 0,
|
|
|
}
|
|
}
|
|
|
render(<SessionNodeItem node={node} currentId={undefined} now={0} onOpen={vi.fn()}
|
|
render(<SessionNodeItem node={node} currentId={undefined} now={0} onOpen={vi.fn()}
|
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
onRename={vi.fn()} onFork={vi.fn()} onArchive={vi.fn()} t={t} />)
|
|
@@ -527,7 +582,7 @@ describe('workspace browser rows', () => {
|
|
|
it('draggable row wires start/end and gates hover/drop on an active same-group drag', () => {
|
|
it('draggable row wires start/end and gates hover/drop on an active same-group drag', () => {
|
|
|
const node: SessionNode = {
|
|
const node: SessionNode = {
|
|
|
id: sid('s1'), title: 'Drag me', blank: false, running: false,
|
|
id: sid('s1'), title: 'Drag me', blank: false, running: false,
|
|
|
- runningSubagentCount: 0, completed: false, updatedAt: 0,
|
|
|
|
|
|
|
+ runningSubagentCount: 0, completed: false, hasActiveSchedule: false, updatedAt: 0,
|
|
|
}
|
|
}
|
|
|
const inactive = dragProps()
|
|
const inactive = dragProps()
|
|
|
const { rerender } = render(
|
|
const { rerender } = render(
|