/** jsdom behavior only; no layout, browser pixels, SDK process or model calls. */ import assert from 'node:assert/strict' import { createRequire } from 'node:module' const externalRequire=createRequire(process.cwd()+'/package.json') const {JSDOM}=externalRequire(process.env.YCODE_TEST_DOM_MODULE||'jsdom') const dom=new JSDOM('
',{url:'http://localhost/',pretendToBeVisual:true}) for(const key of ['window','document','Node','Element','HTMLElement','HTMLInputElement','HTMLTextAreaElement','MouseEvent','KeyboardEvent']) Object.defineProperty(globalThis,key,{value:dom.window[key],configurable:true}) Object.defineProperty(globalThis,'navigator',{value:dom.window.navigator,configurable:true}) Object.assign(globalThis,{IS_REACT_ACT_ENVIRONMENT:true}) const React=require('react') as typeof import('react') const {act}=React const {createRoot}=require('react-dom/client') as typeof import('react-dom/client') const {PermissionConfirm}=require('../src/renderer/src/workbuddy/conversation/PermissionConfirm') as typeof import('../src/renderer/src/workbuddy/conversation/PermissionConfirm') const root=createRoot(document.getElementById('root')!) let count=0,completed=0,declined=0, resolve!:()=>void,reject!:(e:Error)=>void let checks=0 const check=(value:unknown,message:string)=>{assert.ok(value,message);checks++} const options=[{optionId:'once',kind:'allow_once',name:'Once'},{optionId:'session',kind:'allow_always',name:'Session'},{optionId:'deny',kind:'reject_once',name:'Reject'}] const pending=()=>{count++;return new Promise((yes,no)=>{resolve=yes;reject=no})} let id='a',status:'pending'|'cancelled'='pending',defaultToNo=false const render=()=>act(async()=>root.render({declined++}} onDecisionComplete={()=>{completed++}}/>)) const key=(value:string)=>document.querySelector('section')!.dispatchEvent(new KeyboardEvent('keydown',{key:value,bubbles:true,cancelable:true})) const click=(selector:string)=>document.querySelector(selector)!.dispatchEvent(new MouseEvent('click',{bubbles:true,cancelable:true})) async function main(){ await render() check(document.activeElement?.getAttribute('data-option-id')==='once','normal prompt focuses first option') await act(async()=>{key('1');key('1')}) check(count===1,'duplicate keyboard submission synchronous guard') check(document.querySelectorAll('button[disabled]').length===3,'all choices disabled while submitting') await act(async()=>reject(new Error('fixture failed'))) check(document.querySelector('[role="alert"]')?.textContent?.includes('fixture failed'),'failure visible') await act(async()=>click('[data-option-id="once"]')) check(count===2,'failed choice retries') await act(async()=>resolve()) check(completed===1,'success completes once') id='b';defaultToNo=true;await render() check(document.activeElement?.textContent?.includes('Reject'),'default-to-no focuses reject') await act(async()=>key('1'));check(count===2,'default-to-no has no one-key approve shortcut') await act(async()=>key('Escape'));check(declined===1,'Escape rejects') id='c';defaultToNo=false;await render();await act(async()=>click('[data-option-id="once"]')) const oldResolve=resolve;const previous=completed id='d';await render();await act(async()=>oldResolve()) check(completed===previous,'late result cannot complete replacement request') check(document.querySelector('section')?.getAttribute('data-pending-id')==='d','replacement retained') await act(async()=>click('[data-option-id="once"]')) status='cancelled';await render();await act(async()=>resolve()) check(completed===previous,'cancelled request ignores stale success') check(document.querySelector('section')?.textContent?.includes('Cancelled'),'cancelled remains visible') 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') await act(async()=>root.unmount());dom.window.close() console.log(`Permission intercept DOM: ${checks} checks passed (not pixel/SDK verification)`) process.exit(0) } main().catch(error=>{console.error(error);process.exit(1)})