Merge remote-tracking branch 'origin/master' into feat/web-terminal-card
# Conflicts: # packages/client/ui-conversation/README.i18n.yaml
This commit is contained in:
@@ -42,10 +42,10 @@ function assertCompleteCordisLifecycle(events: readonly SessionEvent[]): void {
|
||||
const callIds = new Set(calls.map(event => String(event.data.callId)))
|
||||
const results = events.filter(
|
||||
(event): event is Extract<SessionEvent, { type: 'tool/result' }> =>
|
||||
event.type === 'tool/result' && callIds.has(String(event.data.callId)),
|
||||
event.type === 'tool/result' && callIds.has(String(event.data.message.source.callId)),
|
||||
)
|
||||
expect(results).toHaveLength(CORDIS_TOOLS.length)
|
||||
expect(results.every(event => !event.data.isError)).toBe(true)
|
||||
expect(results.every(event => !event.data.message.content[0].isError)).toBe(true)
|
||||
}
|
||||
|
||||
describe('web e2e: Cordis tools use the generic row variants', () => {
|
||||
|
||||
@@ -87,10 +87,10 @@ describe('web e2e: fresh round trip through the real assembly', () => {
|
||||
const bashCall = sessionEvents.find(event => event.type === 'tool/call' && event.data.name === 'bash')
|
||||
if (bashCall?.type !== 'tool/call') throw new Error('the replayed turn did not call the bash tool')
|
||||
const bashResult = sessionEvents.find(event =>
|
||||
event.type === 'tool/result' && event.data.callId === bashCall.data.callId)
|
||||
event.type === 'tool/result' && event.data.message.source.callId === bashCall.data.callId)
|
||||
if (bashResult?.type !== 'tool/result') throw new Error('the bash tool call produced no durable result')
|
||||
expect(bashResult.data.isError).toBe(false)
|
||||
expect(bashResult.data.content.filter(block => block.type === 'text').map(block => block.text).join(''))
|
||||
expect(bashResult.data.message.content[0].isError).toBe(false)
|
||||
expect(bashResult.data.message.content[0].content.filter(block => block.type === 'text').map(block => block.text).join(''))
|
||||
.toBe('WEB_E2E_OK\n')
|
||||
const turnEnds = sessionEvents.filter(e => e.type === 'turn/end')
|
||||
expect(turnEnds.length).toBe(1)
|
||||
|
||||
@@ -89,8 +89,10 @@ function providerTitle(page: HistoryPage): string | undefined {
|
||||
|
||||
function hasAssistantMarker(page: HistoryPage, marker: string): boolean {
|
||||
return page.events.some(({ event }) => {
|
||||
if (event.type !== 'assistant/message' || !isRecord(event.data) || !Array.isArray(event.data.content)) return false
|
||||
return event.data.content.some(block =>
|
||||
if (event.type !== 'assistant/message' || !isRecord(event.data) || !isRecord(event.data.message)) return false
|
||||
const content = event.data.message.content
|
||||
if (!Array.isArray(content)) return false
|
||||
return content.some(block =>
|
||||
isRecord(block) && block.type === 'text' && typeof block.text === 'string' && block.text.includes(marker))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
- text: 已完成 $ echo NAVIGATION_OK
|
||||
- text: 已完成 {{workspace}} echo NAVIGATION_OK
|
||||
- button "复制"
|
||||
- text: NAVIGATION_OK
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
// Opens the fixture history session and pins the todo_write turn's two
|
||||
// surfaces: the dedicated TodoRow in the chat flow (keyed toolview, summary
|
||||
// derived from the call args) and the TodoPanel plan strip riding the
|
||||
// 'conversation.input.dock' slot (fed by ConversationSnapshot.todos, seeded
|
||||
// by the tail history page), including the collapse interaction.
|
||||
// 'conversation.input.dock' slot (fed by the host `todos` projection via
|
||||
// useProjection, seeded by the tail history page), including the collapse
|
||||
// interaction and the next-turn clearance of the standing plan.
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { act, cleanup, fireEvent, screen, waitFor, within } from '@testing-library/react'
|
||||
@@ -189,3 +190,31 @@ it('collapses the plan strip to the count summary and restores it', async () =>
|
||||
fireEvent.click(header)
|
||||
expect(panel.querySelectorAll('li')).toHaveLength(3)
|
||||
})
|
||||
|
||||
it('hides the plan strip when the next turn starts', async () => {
|
||||
boot()
|
||||
await openFixtureSession()
|
||||
expect(document.querySelector('[data-testid="todo-panel"]')).not.toBeNull()
|
||||
|
||||
const composer = await screen.findByPlaceholderText('Message the agent', {}, { timeout: 10_000 })
|
||||
fireEvent.change(composer, { target: { value: '下一轮清空计划' } })
|
||||
fireEvent.keyDown(composer, { key: 'Enter' })
|
||||
|
||||
await screen.findByText('下一轮清空计划', { exact: true }, { timeout: 10_000 })
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector('[data-testid="todo-panel"]')).toBeNull()
|
||||
}, { timeout: 10_000 })
|
||||
|
||||
expect({
|
||||
promptVisible: screen.getByText('下一轮清空计划', { exact: true }).textContent,
|
||||
panelGone: document.querySelector('[data-testid="todo-panel"]') === null,
|
||||
// Historical todo_write row stays in the flow; only the dock strip clears.
|
||||
rowStillPresent: document.querySelector('[data-sample="todo-row"]') !== null,
|
||||
}).toMatchInlineSnapshot(`
|
||||
{
|
||||
"panelGone": true,
|
||||
"promptVisible": "下一轮清空计划",
|
||||
"rowStillPresent": true,
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user