/** A01 real component DOM regression. Offline only; does not verify browser geometry or SDK execution. */ import assert from 'node:assert/strict' import { createRequire } from 'node:module' import type { ChatMessage } from '../src/shared/types' const { JSDOM } = createRequire(process.cwd() + '/package.json')(process.env.YCODE_TEST_DOM_MODULE || 'jsdom') const dom = new JSDOM('
', {pretendToBeVisual:true,url:'http://localhost/'}) for (const key of ['window','document','Node','Element','HTMLElement','MouseEvent','KeyboardEvent','Event']) 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 MessageBubble = require('../src/renderer/src/components/MessageBubble').default as typeof import('../src/renderer/src/components/MessageBubble').default const root = createRoot(document.getElementById('root')!) const click = (el: Element) => el.dispatchEvent(new MouseEvent('click',{bubbles:true})) const answer = {questions:[{id:'q1',question:'Choose a unique option',answers:['Unique answer']}]} const base: ChatMessage = {id:'native-question',role:'assistant',createdAt:1,content:'Final prose',status:'done',parts:[{type:'tool',toolId:'q'},{type:'text',text:'Final prose'}],tools:[{id:'q',name:'AskUserQuestion',args:{questions:[{id:'q1',question:'Choose a unique option'}]},argsRaw:'{}',status:'ok',output:'RAW SDK ANSWER'}],interactions:[{id:'i',runId:'r',kind:'question',toolCallId:'q',title:'Question',questions:[],status:'answered',answer}]} let checks=0 const check=(condition:unknown,label:string)=>{assert.ok(condition,label);checks++} async function main(){ for (const name of ['AskUserQuestion','askuserquestion','ask_user_question']) { const fixture={...base,tools:base.tools!.map(tool=>({...tool,name})),interactions:[...base.interactions!,...base.interactions!]} const before=JSON.stringify(fixture) await act(async()=>root.render()) check(!document.body.textContent!.includes('RAW SDK ANSWER'),`${name}: no raw answer`) await act(async()=>click(document.querySelector('.cr-collapse--completed > .cr-collapse__header')!)) check(document.querySelectorAll('.ask-user-question--completed').length===1,`${name}: linked duplicate interactions produce one question card`) await act(async()=>click(document.querySelector('.ask-user-question__header .cr-tool-head__main')!)) check(document.querySelector('.ask-user-question__header .cr-tool-head__main')?.getAttribute('aria-expanded')==='true',`${name}: answered card actually expanded`) check(document.querySelectorAll('.question-answer-transcript__question').length===1,`${name}: expanded answer occurs exactly once`) check(document.body.textContent!.includes('Choose a unique option') && document.body.textContent!.includes('Unique answer'),`${name}: question and answer visible`) check(JSON.stringify(fixture)===before,`${name}: recorded source unchanged`) } // Native SDK identity throughout pending → answered → completed → outer toggle. const pending: ChatMessage={...base,status:'streaming',parts:[{type:'tool',toolId:'q'}],interactions:[],tools:base.tools!.map(tool=>({...tool,status:'running',output:undefined}))} await act(async()=>root.render()) check(!!document.querySelector('.ask-user-question--pending'),'native pending question uses specialized renderer') await act(async()=>root.render()) check(!!document.querySelector('.ask-user-question--completed'),'native answered question uses transcript while turn runs') await act(async()=>root.render()) const outer=()=>document.querySelector('.cr-collapse--completed > .cr-collapse__header')! await act(async()=>click(outer())) const header=()=>document.querySelector('.ask-user-question__header .cr-tool-head__main')! if(header().getAttribute('aria-expanded')!=='true') await act(async()=>click(header())) await act(async()=>click(outer()));await act(async()=>click(outer())) check(header().getAttribute('aria-expanded')==='true','native answered choice survives completed outer collapse/reopen') check(!document.body.textContent!.includes('RAW SDK ANSWER'),'native lifecycle never leaks raw SDK answer') const imported={...base,interactions:[],tools:base.tools!.map(tool=>({...tool,output:'q1 -> Unique answer',metadata:{importedHistory:true}}))} await act(async()=>root.render()) await act(async()=>click(document.querySelector('.cr-collapse--completed > .cr-collapse__header')!)) await act(async()=>click(document.querySelector('.ask-user-question__header .cr-tool-head__main')!)) check(document.body.textContent!.includes('Unique answer'),'historical native name and answer-text adaptation preserved') check(imported.tools[0].name==='AskUserQuestion','historical stored name unchanged') for (const interactions of [base.interactions, []]) { const legacy={...base,parts:undefined,interactions,tools:base.tools!.map(tool=>({...tool,metadata:{questionAnswer:answer}}))} await act(async()=>root.render()) check(document.querySelector('.question-history-unpositioned')?.getAttribute('aria-label')==='历史问答(原始位置不可用)','missing recorded position remains explicitly unknown') check(document.querySelectorAll('.question-answer-transcript__question').length===1,'unknown-position metadata and interaction do not duplicate') check(!document.body.textContent!.includes('RAW SDK ANSWER'),'unknown-position raw duplicate suppressed') } await act(async()=>root.unmount());dom.window.close() console.log(`question-identity: ${checks} A01 DOM checks passed (not Electron/pixel/SDK acceptance)`) } main().then(() => process.exit(0)).catch(error=>{console.error(error);process.exit(1)})