fix(composer): show only approval card during authorization
This commit is contained in:
1 parent
b4757dd843
commit
4a7f17961d
4 files changed
+32
-34
No files matched your search
@@ -55,8 +55,7 @@ async function main(){
|
||||
id='e';status='pending';await render();await act(async()=>click('.conversation-intercept-card__rule-action'))
|
||||
check(document.querySelector('[role="status"]')?.textContent?.includes('未提供命中规则详情'),'missing rule explanation explicit')
|
||||
// Exercise the real composer with the real approval surface, not a skeleton.
|
||||
let session='session-a', active=true, sends=0, stops=0, changes=0, stopFails=false, request='request-1', stopPending=false
|
||||
let rejectStop!: (error: Error)=>void
|
||||
let session='session-a', active=true, sends=0, stops=0, changes=0, stopFails=false, request='request-1'
|
||||
let settled: 'answered'|'rejected'|undefined
|
||||
const draft='preserved draft'
|
||||
const attachments=[{id:'attachment-a',name:'a.txt',mimeType:'text/plain',size:1}]
|
||||
@@ -65,7 +64,7 @@ async function main(){
|
||||
const renderComposer=()=>act(async()=>{
|
||||
const composition=composeComposer({interaction:active?{id:session,kind:'approval',status:'pending'}:null,queueCount:2,running:true})
|
||||
root.render(<ComposerControls key={session} sessionId={session} value={draft} onChange={()=>{changes++}} onSend={()=>{sends++}}
|
||||
attachments={attachments} resources={resources} quotes={quotes} onStop={()=>{stops++;if(stopFails)throw new Error('stop fixture failed');if(stopPending)return new Promise<void>((_yes,no)=>{rejectStop=no})}}
|
||||
attachments={attachments} resources={resources} quotes={quotes} onStop={async()=>{stops++;if(stopFails)throw new Error('stop fixture failed')}}
|
||||
queueSlot={composition.queue.visible?<div data-queue="true">two queued messages</div>:undefined}
|
||||
approvalKey={request} approvalSlot={!composition.input.visible?<PermissionConfirm pendingId={session} toolName="Bash" options={options} resolve={pending} reject={async()=>{declined++}} onDecisionComplete={decision=>{settled=decision}}/>:undefined}/>)
|
||||
})
|
||||
@@ -75,6 +74,8 @@ async function main(){
|
||||
check(getComputedStyle(stack).display==='none','composer CSS removes the normal input from layout during approval')
|
||||
check(document.querySelectorAll('[data-composer-approval] [role="dialog"]').length===1,'one bottom approval surface')
|
||||
check(!document.querySelector('.wb-composer__toolbar'),'normal toolbar and send controls removed during approval')
|
||||
check(!document.querySelector('button[aria-label="停止当前运行"],button[aria-label="停止"],button[aria-label="发送"],.wb-composer__approval-stop'),'approval takeover has no standalone stop or send button even with onStop supplied')
|
||||
check(document.querySelector('[data-composer-approval]')?.children.length===1,'bottom approval surface contains only the approval card')
|
||||
check(!document.querySelector('[data-queue]'),'queue hidden without deleting queue state')
|
||||
const editor=stack.querySelector<HTMLElement>('[contenteditable]')!
|
||||
check(editor?.getAttribute('contenteditable')==='false','rich editor disabled during approval')
|
||||
@@ -99,18 +100,21 @@ async function main(){
|
||||
active=true;await renderComposer();await act(async()=>click('[data-option-id="once"]'));const staleResolve=resolve
|
||||
session='session-b';active=false;settled=undefined;await renderComposer();await act(async()=>staleResolve())
|
||||
check(!document.querySelector('[data-composer-approval]')&&settled===undefined,'session switch unmounts old request and ignores its late result')
|
||||
active=true;stopFails=true;await renderComposer();await act(async()=>click('.wb-composer__approval-stop'))
|
||||
check(document.querySelector('[data-composer-approval] [role="alert"]')?.textContent?.includes('stop fixture failed'),'failed cancellation visible in approval surface')
|
||||
request='request-2';stopFails=false;await renderComposer()
|
||||
check(!document.querySelector('[data-composer-approval] [role="alert"]'),'next request clears previous cancellation failure')
|
||||
await act(async()=>click('.wb-composer__approval-stop'))
|
||||
check(!document.querySelector('[data-composer-approval] [role="alert"]'),'cancellation retry clears error')
|
||||
check(stops===2,'stop remains reachable independently of normal send')
|
||||
stopPending=true;await renderComposer();await act(async()=>click('.wb-composer__approval-stop'))
|
||||
check(document.querySelector<HTMLButtonElement>('.wb-composer__approval-stop')?.disabled,'pending stop disabled against duplicate cancellation')
|
||||
request='request-3';stopPending=false;await renderComposer();await act(async()=>rejectStop(new Error('obsolete stop failure')))
|
||||
check(!document.querySelector('[data-composer-approval] [role="alert"]')&&!document.querySelector<HTMLButtonElement>('.wb-composer__approval-stop')?.disabled,'late previous-request cancellation cannot alter new request')
|
||||
active=false;await renderComposer();check(!document.querySelector('[data-composer-approval]'),'cancelled interaction restores ordinary input')
|
||||
// Stopping remains a normal-composer/parent action, never an extra approval control.
|
||||
stopFails=true;await renderComposer();await act(async()=>click('.wb-composer__queue-stop'))
|
||||
check(document.querySelector('[role="alert"]')?.textContent?.includes('stop fixture failed'),'normal composer still reports stop failure')
|
||||
stopFails=false;await act(async()=>click('.wb-composer__queue-stop'))
|
||||
check(stops===2,'normal composer stop remains reachable and can retry independently of send')
|
||||
active=true;await renderComposer()
|
||||
const previousApproval=document.querySelector('[data-composer-approval]')
|
||||
check(!document.querySelector('[data-composer-approval] [role="alert"]'),'normal stop failure does not leak into approval surface')
|
||||
check(!document.querySelector('button[aria-label="停止当前运行"],button[aria-label="停止"],.wb-composer__approval-stop'),'approval has no standalone cancellation control')
|
||||
request='request-2';await renderComposer()
|
||||
check(document.querySelector('[data-composer-approval]')!==previousApproval,'replacement request remounts keyed approval wrapper')
|
||||
check(stops===2&&sends===0,'approval takeover and replacement neither stop the run nor send a message')
|
||||
// Parent cancellation removes the pending interaction without depending on approval stop UI.
|
||||
active=false;await renderComposer();check(!document.querySelector('[data-composer-approval]'),'parent-cancelled interaction restores ordinary input')
|
||||
check(!!document.querySelector('.wb-composer__queue-stop')&&!!document.querySelector('[data-queue]'),'normal stop and queue restore after parent cancellation')
|
||||
// An upload already started before takeover must finish into the same draft without stealing focus.
|
||||
let uploaded: typeof attachments=[], stagingResolve!: (value: typeof attachments[number])=>void, uploadActive=false
|
||||
const uploadHost={pick:async()=>[],stage:()=>new Promise<typeof attachments[number]>(yes=>{stagingResolve=yes})}
|
||||
@@ -129,6 +133,15 @@ async function main(){
|
||||
check(!document.querySelector('.conversation-input-area__input-stack')!.contains(document.activeElement),'upload completion does not focus hidden editor')
|
||||
uploadActive=false;await renderUpload()
|
||||
check(document.querySelector('.conversation-input-area__input-stack')?.textContent?.includes('upload.txt'),'completed attachment restored after approval')
|
||||
// Empty drafts restore the ordinary stop button, not a send button, after takeover.
|
||||
const renderEmpty=(approval:boolean)=>act(async()=>root.render(<ComposerControls key="empty-session" sessionId="empty-session" value="" onChange={()=>{}} onSend={()=>{sends++}} onStop={async()=>{stops++}}
|
||||
approvalKey="empty-approval" approvalSlot={approval?<PermissionConfirm pendingId="empty-approval" toolName="Bash" options={options} resolve={pending} reject={async()=>{}}/>:undefined}/>))
|
||||
await renderEmpty(true)
|
||||
check(!document.querySelector('button[aria-label="停止"],button[aria-label="发送"]'),'empty draft also has no normal stop or send during approval')
|
||||
await renderEmpty(false)
|
||||
check(!!document.querySelector('button[aria-label="停止"]')&&!document.querySelector('button[aria-label="发送"]'),'empty draft restores normal stop without send after approval')
|
||||
await act(async()=>click('button[aria-label="停止"]'))
|
||||
check(stops===3&&sends===0,'restored empty-draft stop still invokes cancellation without sending')
|
||||
await act(async()=>root.unmount());dom.window.close()
|
||||
console.log(`Permission intercept DOM: ${checks} checks passed (not pixel/SDK verification)`)
|
||||
process.exit(0)
|
||||
|
||||
Reference in new issue
Block a user