Files
deepseek-harness/packages/client/ui-trajectory/tests/virtual-rows.client.spec.ts
T
Chinesezjc b462d5fd69 Merge remote-tracking branch 'origin/master' into feat/web-message-feedback-ui
Adapt to two contract changes master introduced:

- The generated Remote face now wraps every business result in
  RemoteResult, folding carrier failures into an ok:false branch instead
  of rejecting. The controller reads that envelope at its three call
  sites and maps a carrier failure onto the same settled shape the
  controls already render; three specs cover the new branch.
- Client packages split their tsconfig into host and client halves, and
  the host aggregate now compiles any test not named *.client.spec.*.
  Rename this package's specs to the client convention and drop the
  ../connection project reference, which pointed at a solution file that
  no longer carries the client sources.

Keep master's mount loop with its rollback-on-failure in api-remotes and
add messageFeedbackRemote to it.
2026-08-12 10:43:23 +08:00

96 lines
3.2 KiB
TypeScript

/** Measurable virtual-row grouping and durable identity contracts. */
import { describe, expect, it } from 'vitest'
import type { TrajectoryCellProps } from '../src/client/trajectory-record.ts'
import {
groupTrajectoryVirtualRows, trajectoryVirtualRecordKey,
type VirtualizableTrajectoryRecord,
} from '../src/client/trajectory-virtual-rows.ts'
function record(
index: number,
cell: Partial<TrajectoryCellProps> = {},
collapsedSummaryKind?: 'turn' | 'assistant',
): VirtualizableTrajectoryRecord {
return {
cell: {
index,
kind: 'message',
text: `record ${index}`,
timeSeconds: 0,
...cell,
},
...(collapsedSummaryKind === undefined ? {} : { collapsedSummaryKind }),
}
}
describe('trajectory virtual rows', () => {
it('groups zero-height request boundaries with the following content row', () => {
const first = record(1, { requestOnly: true, sourceSeq: 10 })
const second = record(2, { requestOnly: true, sourceSeq: 11 })
const content = record(3, { sourceSeq: 12 })
expect(groupTrajectoryVirtualRows([first, second, content])).toEqual([{
entries: [
{ logicalIndex: 0, record: first },
{ logicalIndex: 1, record: second },
{ logicalIndex: 2, record: content },
],
height: 30,
key: trajectoryVirtualRecordKey(content),
}])
})
it('retains terminal request-boundary clearance as a measurable row', () => {
const content = record(1, { sourceSeq: 10 })
const boundary = record(2, { requestOnly: true, sourceSeq: 11 })
const rows = groupTrajectoryVirtualRows([content, boundary])
expect(rows).toHaveLength(2)
expect(rows[1]).toEqual({
entries: [{ logicalIndex: 1, record: boundary }],
height: 9,
key: trajectoryVirtualRecordKey(boundary),
})
})
it('uses the rendered collapsed-summary height', () => {
const summary = record(1, { sourceSeq: 10 }, 'turn')
expect(groupTrajectoryVirtualRows([summary])[0]?.height).toBe(20)
})
it('keeps an existing row key stable when older history is prepended', () => {
const existing = record(2, { sourceSeq: 100 })
const prepended = record(1, { sourceSeq: 10 })
const before = groupTrajectoryVirtualRows([existing])[0]?.key
const after = groupTrajectoryVirtualRows([prepended, existing])[1]?.key
expect(after).toBe(before)
})
it('keeps the content key when a request boundary joins its row', () => {
const content = record(2, { sourceSeq: 100 })
const boundary = record(1, { requestOnly: true, sourceSeq: 99 })
expect(groupTrajectoryVirtualRows([boundary, content])[0]?.key)
.toBe(groupTrajectoryVirtualRows([content])[0]?.key)
})
it('distinguishes a folded summary from its source record', () => {
const source = record(1, { sourceSeq: 10 })
const summary = record(1, { sourceSeq: 10 }, 'assistant')
expect(trajectoryVirtualRecordKey(summary)).not.toBe(trajectoryVirtualRecordKey(source))
})
it('exposes a DOM-safe semantic key', () => {
const source = record(1, { callId: 'call with spaces/and?punctuation' })
expect(trajectoryVirtualRecordKey(source)).toBe(
'message%00call%00call%20with%20spaces%2Fand%3Fpunctuation',
)
})
})