Wait for settled material notices in browser checks

Why: A failed task card can appear before its notification turn, and the second finding must be observed while work is still running.

Evidence: The strengthened browser checks first exposed missing fixture notice text and a zero-width second-stage window; after fixture correction all six task-owned HTTP/Playwright scenarios passed. Frontend 265 unit tests and build passed.

Impact: Test coverage only; no production UI change.

Refs: U3 r0.4 independent review Important 5
This commit is contained in:
2026-10-14 03:57:38 +00:00
parent 34950997f8
commit 01ec12e77c
+16 -2
View File
@@ -25,7 +25,19 @@ async function setup(request:any,scenario:Scenario='normal',awaitTask=true){
const out=await(await request.get(manifest.base+`/api/conversations/${conv.id}/cards`)).json()
return out.cards.find((c:any)=>c.task)?.task.executionState
}).toBe(scenario.endsWith('failure')?'failed':'completed')
return {project,conv,ids}
return {project,conv,ids,sendRunId:sendBody.run.id}
}
async function waitForFailureNotice(request:any,seed:any){
await expect.poll(async()=>{
const response=await request.get(manifest.base+`/api/conversations/${seed.conv.id}`)
if(!response.ok())return false
const current=await response.json()
const children=current.runs.filter((r:any)=>r.kind==='subagent')
const notices=current.messages.filter((m:any)=>m.role==='system'&&m.parts?.some((p:any)=>p.type==='text'&&p.text?.includes('后台子代理「')))
const noticeRuns=current.runs.filter((r:any)=>r.kind==='conversation'&&r.id!==seed.sendRunId)
return children.length>0&&notices.length>=children.length&&noticeRuns.length>=children.length&&
noticeRuns.every((r:any)=>['completed','failed','interrupted'].includes(r.status))
}).toBe(true)
}
async function open(page:any,seed:any){
await page.addInitScript(({id}:{id:string})=>{localStorage.setItem('as.conv',id);localStorage.setItem('as.ui',JSON.stringify({theme:'light',rightCollapsed:true,fontSize:14}))},{id:seed.conv.id})
@@ -69,19 +81,21 @@ test('two sourced stages appear while the material task is still running',async(
const cards=async()=>(await(await request.get(manifest.base+`/api/conversations/${seed.conv.id}/cards`)).json()).cards
await expect.poll(async()=>{const card=(await cards()).find((c:any)=>c.task);return card?.task.executionState}).toBe('running')
await expect(page.locator('[data-conversation-work]')).toContainText('上线六月')
await expect.poll(async()=>{const card=(await cards()).find((c:any)=>c.task);return card?.task.findings?.length}).toBeGreaterThan(1)
await expect.poll(async()=>{const card=(await cards()).find((c:any)=>c.task);return card?.task.executionState==='running'&&card.task.checkpointSeq>=2&&card.task.findings?.length>1}).toBe(true)
await expect(page.locator('[data-conversation-work]')).toContainText('验收条件需要补齐')
await expect.poll(async()=>{const card=(await cards()).find((c:any)=>c.task);return card?.task.executionState}).toBe('completed')
})
for(const scenario of ['zero-failure','partial-failure'] as const){
test(`${scenario} shows the saved scope without a deliverable choice`,async({page,request})=>{
const seed=await setup(request,scenario)
await waitForFailureNotice(request,seed)
await page.addInitScript(({id}:{id:string})=>{localStorage.setItem('as.conv',id);localStorage.setItem('as.ui',JSON.stringify({theme:'light',rightCollapsed:true,fontSize:14}))},{id:seed.conv.id})
await page.goto('/')
const work=page.locator('[data-conversation-work]')
await expect(work).toContainText('执行失败')
if(scenario==='zero-failure')await expect(work).toContainText('暂无阶段结论记录')
else await expect(work).toContainText('上线六月')
await expect(page.getByText('后台整理状态已回投工作卡;请按卡片中的结果继续。').last()).toBeVisible()
await expect(page.locator('.hitl-choice')).toHaveCount(0)
})
}