|
|
@@ -1,6 +1,6 @@
|
|
|
// @vitest-environment jsdom
|
|
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
|
-import { cleanup, fireEvent, render, screen } from '@testing-library/react'
|
|
|
+import { cleanup, fireEvent, render, screen, within } from '@testing-library/react'
|
|
|
import { makeTranslate } from '@deepseek-ai/dsh-client-test-runtime'
|
|
|
import type {
|
|
|
SessionId, SessionListState, SessionSummary, SubagentCatalogSnapshot,
|
|
|
@@ -151,6 +151,7 @@ describe('SubagentCatalogAction', () => {
|
|
|
expect(diagnostic.getAttribute('aria-disabled')).toBe('true')
|
|
|
expect(screen.getByRole('button', { name: '展开 worker 的下级子代理' })).toBeTruthy()
|
|
|
expect(screen.queryByRole('button', { name: '展开 reviewer 的下级子代理' })).toBeNull()
|
|
|
+ expect(screen.getByRole('treeitem', { name: /reviewer/ }).children).toHaveLength(2)
|
|
|
|
|
|
fireEvent.click(screen.getByRole('treeitem', { name: /worker/ }))
|
|
|
expect(input.openChild).toHaveBeenCalledWith({
|
|
|
@@ -177,6 +178,19 @@ describe('SubagentCatalogAction', () => {
|
|
|
expect(translate).toHaveBeenCalledWith('count.total.one', { count: 1 })
|
|
|
})
|
|
|
|
|
|
+ it('removes the disclosure column from branchless catalog levels', () => {
|
|
|
+ const input = props(catalog({
|
|
|
+ entries: [{
|
|
|
+ kind: 'child', id: CHILD, mode: 'continuable', label: 'worker',
|
|
|
+ activity: 'running', hasChildren: false,
|
|
|
+ }],
|
|
|
+ }))
|
|
|
+ render(<SubagentCatalogAction {...input} />)
|
|
|
+ fireEvent.click(screen.getByRole('button', { name: /1 个子代理/ }))
|
|
|
+
|
|
|
+ expect(screen.getByRole('treeitem', { name: /worker/ }).children).toHaveLength(1)
|
|
|
+ })
|
|
|
+
|
|
|
it('supports trigger/menu keyboard traversal, Escape focus restore, and outside close', async () => {
|
|
|
const input = props(catalog())
|
|
|
render(<SubagentCatalogAction {...input} />)
|
|
|
@@ -240,7 +254,7 @@ describe('SubagentCatalogAction', () => {
|
|
|
})
|
|
|
})
|
|
|
|
|
|
- it('ticks active duration by seconds and freezes inactive rows', async () => {
|
|
|
+ it('shows durable token totals, ticks active duration by seconds, and freezes inactive rows', async () => {
|
|
|
const now = 2_000_000_000_000
|
|
|
const minute = 60_000
|
|
|
const hour = 60 * minute
|
|
|
@@ -258,6 +272,26 @@ describe('SubagentCatalogAction', () => {
|
|
|
['years', 'inactive', 832 * day, undefined, undefined, now],
|
|
|
['whole-year', 'inactive', 365 * day, undefined, undefined, now],
|
|
|
] as const
|
|
|
+ const usageById = {
|
|
|
+ running: {
|
|
|
+ uncachedInputTokens: 1_000,
|
|
|
+ outputTokens: 200,
|
|
|
+ cacheReadTokens: 3_000,
|
|
|
+ cacheWriteTokens: 400,
|
|
|
+ },
|
|
|
+ finished: {
|
|
|
+ uncachedInputTokens: 123,
|
|
|
+ outputTokens: 0,
|
|
|
+ cacheReadTokens: 0,
|
|
|
+ cacheWriteTokens: 0,
|
|
|
+ },
|
|
|
+ interrupted: {
|
|
|
+ uncachedInputTokens: 123_000_000,
|
|
|
+ outputTokens: 0,
|
|
|
+ cacheReadTokens: 0,
|
|
|
+ cacheWriteTokens: 0,
|
|
|
+ },
|
|
|
+ } as const
|
|
|
const entries = rows.map(([id, activity]) => ({
|
|
|
kind: 'child' as const,
|
|
|
id: id as SessionId,
|
|
|
@@ -282,6 +316,9 @@ describe('SubagentCatalogAction', () => {
|
|
|
? {}
|
|
|
: { active: { since: activeSince, through: activeThrough } }),
|
|
|
},
|
|
|
+ tokenUsage: id in usageById
|
|
|
+ ? usageById[id as keyof typeof usageById]
|
|
|
+ : undefined,
|
|
|
},
|
|
|
}]
|
|
|
})) as Record<SessionId, SessionSummary>
|
|
|
@@ -289,9 +326,14 @@ describe('SubagentCatalogAction', () => {
|
|
|
render(<SubagentCatalogAction {...input} />)
|
|
|
fireEvent.click(screen.getByRole('button', { name: /9 个子代理/ }))
|
|
|
|
|
|
- expect(screen.getByRole('treeitem', { name: /running.*1分10秒/ })).toBeTruthy()
|
|
|
- expect(screen.getByRole('treeitem', { name: /finished.*1小时02分03秒/ })).toBeTruthy()
|
|
|
- expect(screen.getByRole('treeitem', { name: /interrupted.*6秒/ })).toBeTruthy()
|
|
|
+ const runningRow = screen.getByRole('treeitem', { name: /running.*4\.6K tok · 1分10秒/ })
|
|
|
+ const runningMetrics = within(runningRow)
|
|
|
+ const tokenMetric = runningMetrics.getByText('4.6K tok')
|
|
|
+ const durationMetric = runningMetrics.getByText('1分10秒')
|
|
|
+ expect(tokenMetric.parentElement).toBe(durationMetric.parentElement)
|
|
|
+ expect(tokenMetric.nextElementSibling).toBe(durationMetric)
|
|
|
+ expect(screen.getByRole('treeitem', { name: /finished.*123 tok · 1小时02分03秒/ })).toBeTruthy()
|
|
|
+ expect(screen.getByRole('treeitem', { name: /interrupted.*123M tok · 6秒/ })).toBeTruthy()
|
|
|
expect(screen.getByRole('treeitem', { name: /days.*12天05小时06分07秒/ })).toBeTruthy()
|
|
|
expect(screen.getByText('12天5小时').getAttribute('title'))
|
|
|
.toBe('总活跃耗时:12天05小时06分07秒')
|
|
|
@@ -302,9 +344,9 @@ describe('SubagentCatalogAction', () => {
|
|
|
expect(screen.getByText('约1年')).toBeTruthy()
|
|
|
|
|
|
await vi.advanceTimersByTimeAsync(1_000)
|
|
|
- expect(screen.getByRole('treeitem', { name: /running.*1分11秒/ })).toBeTruthy()
|
|
|
- expect(screen.getByRole('treeitem', { name: /finished.*1小时02分03秒/ })).toBeTruthy()
|
|
|
- expect(screen.getByRole('treeitem', { name: /interrupted.*6秒/ })).toBeTruthy()
|
|
|
+ expect(screen.getByRole('treeitem', { name: /running.*4\.6K tok · 1分11秒/ })).toBeTruthy()
|
|
|
+ expect(screen.getByRole('treeitem', { name: /finished.*123 tok · 1小时02分03秒/ })).toBeTruthy()
|
|
|
+ expect(screen.getByRole('treeitem', { name: /interrupted.*123M tok · 6秒/ })).toBeTruthy()
|
|
|
})
|
|
|
|
|
|
it('lazily expands and collapses descendant catalogs with direct-parent navigation', () => {
|