mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
test(gui): todo display fixture sample + browser acceptance script
fx-alpha gains turn 63: a todo_write call/result pair plus the todo/write snapshot event, feeding both the TodoRow toolview and the TodoPanel strip in ?fixture mode. verify-todo-display.mjs drives chromium through panel visibility, content, row summary, details linkage, collapse and dark.
This commit is contained in:
@@ -124,6 +124,18 @@ function buildAlphaLog(): SessionEvent[] {
|
||||
toolTurn(61, 'fx-write', '{"path":"notes/demo.txt","content":"hello fixture\\n"}', 'wrote notes/demo.txt')
|
||||
toolTurn(62, 'edit', '{"file_path":"notes/demo.txt","old_string":"hello","new_string":"hello fixture"}', '已编辑')
|
||||
toolTurn(63, 'write', '{"file_path":"notes/new-demo.txt","content":"hello fixture\\n"}', '已写入')
|
||||
// Turn 64: todo_write sample — the TodoRow toolview in the flow plus the
|
||||
// todo/write snapshot event feeding the TodoPanel plan strip.
|
||||
const fixtureTodos = [
|
||||
{ content: '梳理需求', status: 'completed' },
|
||||
{ content: '实现 fixture 样本', status: 'in_progress' },
|
||||
{ content: '浏览器验收', status: 'pending' },
|
||||
]
|
||||
const todoArgs = JSON.stringify({ todos: fixtureTodos })
|
||||
toolTurn(64, 'todo_write', todoArgs, 'Updated todo list: 1 pending, 1 in progress, 1 completed.')
|
||||
// The tool appends the snapshot event inside its own turn; splice it before the trailing turn/end.
|
||||
events.splice(events.length - 1, 0, { type: 'todo/write', time: time += 800, data: { todos: fixtureTodos } })
|
||||
events.forEach((e, i) => { e.seq = i })
|
||||
return events as unknown as SessionEvent[]
|
||||
}
|
||||
|
||||
|
||||
107
scripts/verify-todo-display.mjs
Normal file
107
scripts/verify-todo-display.mjs
Normal file
@@ -0,0 +1,107 @@
|
||||
// Manual acceptance probe: boot the real shell + 8 bundles in ?fixture mode,
|
||||
// open fx-alpha, assert the TodoPanel strip and the todo_write row render.
|
||||
import { existsSync } from 'node:fs'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { createRequire } from 'node:module'
|
||||
import { startWebServer } from '@deepseek-ai/dsh-host-webserver'
|
||||
|
||||
// playwright is a dependency of apps/web (the browser test owner), not the root.
|
||||
const { chromium } = createRequire(new URL('../apps/web/package.json', import.meta.url)).call(undefined, 'playwright')
|
||||
|
||||
const root = fileURLToPath(new URL('..', import.meta.url))
|
||||
const bundle = (dir) => `${root}packages/client/${dir}/lib/client.js`
|
||||
const PLUGINS = [
|
||||
{ id: '@deepseek-ai/dsh-client-connection', dir: 'connection', inject: [], immediately: true },
|
||||
{ id: '@deepseek-ai/dsh-client-runtime', dir: 'runtime', inject: ['@deepseek-ai/dsh-client-connection'], immediately: true },
|
||||
{ id: '@deepseek-ai/dsh-client-ui-theme', dir: 'ui-theme', inject: [], immediately: true },
|
||||
{ id: '@deepseek-ai/dsh-client-i18n', dir: 'i18n', inject: [], immediately: true },
|
||||
{ id: '@deepseek-ai/dsh-client-ui-layout', dir: 'ui-layout', inject: ['@deepseek-ai/dsh-client-runtime'] },
|
||||
{ id: '@deepseek-ai/dsh-client-ui-sidebar', dir: 'ui-sidebar', inject: ['@deepseek-ai/dsh-client-ui-layout'] },
|
||||
{ id: '@deepseek-ai/dsh-client-ui-conversation', dir: 'ui-conversation', inject: ['@deepseek-ai/dsh-client-ui-layout'] },
|
||||
{ id: '@deepseek-ai/dsh-client-ui-trajectory', dir: 'ui-trajectory', inject: ['@deepseek-ai/dsh-client-ui-conversation'] },
|
||||
]
|
||||
for (const p of PLUGINS) if (!existsSync(bundle(p.dir))) throw new Error(`bundle missing: ${p.dir}`)
|
||||
|
||||
const rows = PLUGINS.map(p => ({
|
||||
id: p.id, url: `/plugins/${p.id}/client.js?rev=verify`, rev: 'verify',
|
||||
...(p.inject.length > 0 ? { inject: p.inject } : {}),
|
||||
...(p.immediately ? { immediately: true } : {}),
|
||||
}))
|
||||
const graph = { rev: 'verify', entries: rows }
|
||||
const byId = new Map(PLUGINS.map(p => [p.id, bundle(p.dir)]))
|
||||
const port = 34567
|
||||
const errors = []
|
||||
const server = await startWebServer({
|
||||
host: '127.0.0.1',
|
||||
port,
|
||||
distIndex: `${root}apps/web/dist/index.html`,
|
||||
apiHandler: { fetch: () => Promise.resolve(new Response('fixture mode must not call /api', { status: 500 })) },
|
||||
webPlugins: { graph: () => graph, clientPath: (id) => byId.get(id), onRebuilt: () => () => undefined },
|
||||
}, (err) => errors.push(`server: ${String(err)}`))
|
||||
|
||||
const browser = await chromium.launch()
|
||||
const page = await browser.newPage({ viewport: { width: 1680, height: 1000 } })
|
||||
page.on('pageerror', e => errors.push(String(e)))
|
||||
await page.goto(`http://127.0.0.1:${port}/?fixture`, { waitUntil: 'load' })
|
||||
try {
|
||||
await page.waitForSelector('[class*="frame"]', { timeout: 15000 })
|
||||
} catch (e) {
|
||||
console.error('BODY:', (await page.evaluate(() => document.body.innerText)).slice(0, 400))
|
||||
console.error('STATUS:', await page.evaluate(() => JSON.stringify(globalThis
|
||||
|
||||
.__DSH_LOADER_STATUS__ ?? 'n/a')))
|
||||
console.error('BOOT:', await page.evaluate(() => JSON.stringify(window.__DSH_BOOT__)))
|
||||
const reqs = await page.evaluate(() => performance.getEntriesByType('resource').map(r => `${r.name.split('/').slice(-2).join('/')}=${r.responseStatus ?? '?'}`))
|
||||
console.error('RES:', reqs.join(' '))
|
||||
console.error('ERRORS:', errors.join(' ;; '))
|
||||
throw e
|
||||
}
|
||||
|
||||
// Open fx-alpha: expand the workspace group, then click the newest session row.
|
||||
await page.locator('[role="treeitem"]').first().click()
|
||||
// fx-alpha is the newest (running) session — the first option row.
|
||||
const sessionRow = page.locator('[role="treeitem"][aria-selected]').first()
|
||||
await sessionRow.waitFor({ timeout: 5000 })
|
||||
await sessionRow.click()
|
||||
await page.waitForSelector('[data-testid="todo-panel"]', { timeout: 15000 })
|
||||
console.log('✓ TodoPanel visible')
|
||||
|
||||
const panelText = await page.locator('[data-testid="todo-panel"]').innerText()
|
||||
for (const expected of ['Plan', '1/3', '梳理需求', '实现 fixture 样本', '浏览器验收']) {
|
||||
if (!panelText.includes(expected)) throw new Error(`TodoPanel missing "${expected}"; got: ${panelText}`)
|
||||
}
|
||||
console.log('✓ TodoPanel content: counts + all three items')
|
||||
|
||||
await page.screenshot({ path: `${root}.artifacts/todo-01-panel.png` })
|
||||
|
||||
// The todo_write row in the flow (turn 63 sample, already at the bottom).
|
||||
const row = page.locator('[data-sample="todo-row"]')
|
||||
await row.waitFor({ timeout: 10000 })
|
||||
const rowText = await row.innerText()
|
||||
if (!rowText.includes('更新任务清单') || !rowText.includes('1/3 已完成')) throw new Error(`TodoRow wrong: ${rowText}`)
|
||||
console.log('✓ TodoRow renders plan summary:', rowText.replace(/\n/g, ' '))
|
||||
await page.screenshot({ path: `${root}.artifacts/todo-02-row.png` })
|
||||
|
||||
// Row click opens details with the raw args.
|
||||
await row.click()
|
||||
await page.waitForSelector('text=Input', { timeout: 5000 })
|
||||
console.log('✓ TodoRow click opens details')
|
||||
await page.screenshot({ path: `${root}.artifacts/todo-03-details.png` })
|
||||
|
||||
// Collapse: list hides, active item hint appears in the header.
|
||||
await page.locator('[data-testid="todo-panel"] button').first().click()
|
||||
const collapsed = await page.locator('[data-testid="todo-panel"]').innerText()
|
||||
if (collapsed.includes('梳理需求')) throw new Error('collapse failed: list still visible')
|
||||
if (!collapsed.includes('实现 fixture 样本')) throw new Error('collapsed hint missing the active item')
|
||||
console.log('✓ Collapse hides list, shows active hint')
|
||||
await page.screenshot({ path: `${root}.artifacts/todo-04-collapsed.png` })
|
||||
|
||||
// Dark theme spot check.
|
||||
await page.evaluate(() => document.body.setAttribute('data-ds-dark-theme', ''))
|
||||
await page.screenshot({ path: `${root}.artifacts/todo-05-dark.png` })
|
||||
console.log('✓ Dark screenshot taken')
|
||||
|
||||
if (errors.length > 0) throw new Error(`page errors: ${errors.join('; ')}`)
|
||||
console.log('✓ No page errors — todo display acceptance PASSED')
|
||||
await browser.close()
|
||||
await server.close()
|
||||
Reference in New Issue
Block a user