/** Execute in an isolated Creator browser preview, never a live player session. */ export async function verifyTaskListPreview() { const e=await System.import('cc'); const service=e.director.getScene().getChildByName('PlatformServices'); const owner=service?.getComponent('PlatformStartup'); if(owner){owner.unsubscribe?.();owner.runtime?.stop();owner.panel?.dispose();owner.panel=null;} await new Promise((resolve,reject)=>e.director.loadScene('PlatformLobby',err=>err?reject(err):resolve())); const prefab=await new Promise((resolve,reject)=>e.assetManager.loadAny('4b529d70-7a96-479e-b8bc-0a92a5dc33c2',(err,a)=>err?reject(err):resolve(a))); const root=e.instantiate(prefab);root.parent=e.director.getScene().getChildByName('Canvas'); const list=root.getComponent('TaskListView'),checks=[]; const assert=(condition,message)=>{if(!condition)throw Error(message);checks.push(message);}; const data=Array.from({length:6},(_,i)=>({taskid:i+1,title:'完成今日首次对局任务',award:i+2,finish:1,total:4,state:i===1?1:i===2?2:0})); list.render(data,'房卡'); const content=list.scrollView.content,items=content.children.map(n=>n.getComponent('TaskItemView')); assert(items.length===6,'six prefab instances'); assert(content.getComponent(e.UITransform).width===1776,'content measures all six items'); assert(list.scrollView.getMaxScrollOffset().x===592,'two additional items scroll horizontally'); assert(list.scrollView.horizontal&&!list.scrollView.vertical,'horizontal only'); assert(content.parent.getComponent(e.Mask)?.enabled,'viewport mask enabled'); assert(root.getChildByPath('group-10/Spirit176').position.x===0,'viewport centered'); assert(items[0].progressBar.progress===0.25,'progress uses finish divided by total'); assert(items[0].progressBar.barSprite.node.getComponent(e.UITransform).width===50.5,'progress scales bar from left'); assert(items[0].doButton.node.active&&!items[3].doButton.node.active,'only first unfinished entry shows do button'); assert(items[1].claimButton.node.active&&!items[1].progressBar.node.active,'claimable entry replaces progress'); assert(items[2].claimedMark.active&&!items[2].claimButton.node.active,'claimed entry replaces claim button'); const events=[];root.on('task-claim',id=>events.push(['claim',id]));root.on('task-do',id=>events.push(['do',id])); items[0].doButton.node.emit(e.Button.EventType.CLICK);items[1].claimButton.node.emit(e.Button.EventType.CLICK); items[2].claimButton.node.emit(e.Button.EventType.CLICK);items[3].doButton.node.emit(e.Button.EventType.CLICK); assert(JSON.stringify(events)===JSON.stringify([['do',1],['claim',2]]),'only visible actions emit correct task ids'); const old=content.children[0];list.render(data.slice(0,1),'房卡'); assert(content.children.length===1&&old.parent===null,'refresh replaces previous entries'); list.render([],'房卡');assert(content.children.length===0&&content.getComponent(e.UITransform).width===0,'empty list clears extent'); list.render(data,'房卡'); // Destruction is deferred until the end of the frame; include that boundary. await new Promise(resolve=>e.director.once(e.Director.EVENT_AFTER_DRAW,resolve)); await new Promise(resolve=>e.director.once(e.Director.EVENT_AFTER_DRAW,resolve)); assert(!document.body.innerText.includes('TypeError:'),'refresh finishes without deferred destruction errors'); globalThis.__taskQA={e,root,list,data,events,checks}; return {checks}; }