mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
test(web): cover row fork in the real app
This commit is contained in:
@@ -87,13 +87,41 @@ describe('web e2e: message IconActions and clocks on settled history', () => {
|
||||
await compareOrRefreshGolden(UI_EXPECTED, snapshot, MODE)
|
||||
})
|
||||
|
||||
it.skipIf(MODE === 'record')('forks the session through the settled user-message action', async () => {
|
||||
it.skipIf(MODE === 'record')('forks through the settled-message and session-row actions', async () => {
|
||||
onTestFailed(() => saveFailureShot(page, 'web-e2e-message-fork'))
|
||||
await page.getByRole('button', { name: '在新对话中分支' }).first().click()
|
||||
await expect.poll(
|
||||
() => scaffold.ctx.agents.list().find(agent => agent.session.header.parentSession === SessionId(SEED_ID)),
|
||||
{ timeout: 15_000 },
|
||||
).toBeDefined()
|
||||
await expect.poll(
|
||||
() => page.locator('[role="treeitem"]').count(),
|
||||
{ timeout: 10_000 },
|
||||
).toBe(3)
|
||||
await expect.poll(
|
||||
() => page.locator('[role="treeitem"][aria-selected="true"]').count(),
|
||||
{ timeout: 10_000 },
|
||||
).toBe(1)
|
||||
// The row action owns a distinct ui-workspace injection from the message
|
||||
// action above, so exercise both through the loaded app before capture.
|
||||
const sourceRow = page.locator('[role="treeitem"][aria-expanded="true"]').last()
|
||||
const rowBox = await sourceRow.boundingBox()
|
||||
if (rowBox === null) throw new Error('fork source row has no layout box')
|
||||
const actionButton = sourceRow.locator('button[aria-label^="Session actions for "]')
|
||||
await sourceRow.hover({ position: { x: rowBox.width - 16, y: rowBox.height / 2 } })
|
||||
await expect.poll(() => actionButton.isVisible(), { timeout: 2_000 }).toBe(true)
|
||||
const buttonBox = await actionButton.boundingBox()
|
||||
if (buttonBox === null) throw new Error('fork source row action has no layout box')
|
||||
await page.mouse.click(buttonBox.x + buttonBox.width / 2, buttonBox.y + buttonBox.height / 2)
|
||||
await page.getByRole('menuitem', { name: 'Fork session' }).click()
|
||||
await expect.poll(
|
||||
() => scaffold.ctx.agents.list().filter(agent => agent.session.header.parentSession !== undefined).length,
|
||||
{ timeout: 15_000 },
|
||||
).toBe(2)
|
||||
await expect.poll(
|
||||
() => page.locator('[role="treeitem"]').count(),
|
||||
{ timeout: 10_000 },
|
||||
).toBe(4)
|
||||
await expect.poll(
|
||||
() => page.locator('[role="treeitem"][aria-selected="true"]').count(),
|
||||
{ timeout: 10_000 },
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
- tree "Sessions":
|
||||
- treeitem "Ungrouped 2 sessions" [expanded]:
|
||||
- treeitem "Ungrouped 3 sessions" [expanded]:
|
||||
- img
|
||||
- text: Ungrouped 2 sessions
|
||||
- text: Ungrouped 3 sessions
|
||||
- treeitem "Collapse Use the read tool twice 1min" [expanded]:
|
||||
- button "Collapse":
|
||||
- img
|
||||
- text: Use the read tool twice 1min
|
||||
- treeitem "Use the read tool twice now" [selected]
|
||||
- treeitem "Use the read tool twice now"
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
- img
|
||||
- button "在新对话中分支":
|
||||
- img
|
||||
- text: {{clock}} cache hit 98% · 15,962 tokens · 1 turns · 2 steps
|
||||
- text: {{clock}}
|
||||
- textbox "Message the agent"
|
||||
- button "Add attachment":
|
||||
- img
|
||||
@@ -41,3 +41,4 @@
|
||||
- text: deepseek-v4-flash
|
||||
- img
|
||||
- button "Send message" [disabled]
|
||||
- text: 1 turns · 2 steps Tool call {{duration}} Cache hit 98% Input 15.8K tok · Output 135 tok
|
||||
|
||||
@@ -82,7 +82,9 @@ describe('sessions.fork', () => {
|
||||
expect(response.result.ok).toBe(true)
|
||||
if (!response.result.ok) return
|
||||
const child = ctx.sessions.get(response.result.value.sessionId)
|
||||
expect(child?.events.length).toBe(3)
|
||||
expect(child?.events.map(event => event.type)).toEqual([
|
||||
'turn/start', 'user/message', 'turn/end', 'session/end-seed',
|
||||
])
|
||||
expect(child?.header.parentSession).toBe(source.id)
|
||||
expect(child?.header.cwd).toBe('/proj')
|
||||
await ctx.fiber.dispose()
|
||||
@@ -92,13 +94,23 @@ describe('sessions.fork', () => {
|
||||
const ctx = await composed()
|
||||
const source = liveAgent(ctx, 'session-tail', 2, true)
|
||||
const proxy = api(ctx)
|
||||
const expectedTypes = [
|
||||
'turn/start', 'user/message', 'turn/end',
|
||||
'turn/start', 'user/message', 'turn/end',
|
||||
'session/end-seed',
|
||||
]
|
||||
const omitted = await proxy.sessions.fork(request({ sessionId: source.id }))
|
||||
expect(omitted.result.ok).toBe(true)
|
||||
if (omitted.result.ok) {
|
||||
expect(ctx.sessions.get(omitted.result.value.sessionId)?.events.length).toBe(6)
|
||||
expect(ctx.sessions.get(omitted.result.value.sessionId)?.events.map(event => event.type))
|
||||
.toEqual(expectedTypes)
|
||||
}
|
||||
const pastEnd = await proxy.sessions.fork(request({ sessionId: source.id, atSeq: 999 }))
|
||||
expect(pastEnd.result.ok).toBe(true)
|
||||
if (pastEnd.result.ok) {
|
||||
expect(ctx.sessions.get(pastEnd.result.value.sessionId)?.events.map(event => event.type))
|
||||
.toEqual(expectedTypes)
|
||||
}
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user