Files
Perler_beads/tools/verify-transition-errors.cjs
2026-10-20 13:13:03 +08:00

61 lines
4.3 KiB
JavaScript

const {chromium}=require('playwright'),fs=require('fs'),assert=require('assert');
const version=process.argv[2]||'pass14';
const out=version==='pass15'?'artifacts/character-tools-20261017/pass15':'artifacts/ui-transition-20261017/pass14';
(async()=>{
const b=await chromium.launch({channel:'chrome',headless:true}),p=await b.newPage({viewport:{width:640,height:1136}}),errors=[];
p.on('pageerror',e=>errors.push(String(e)));
await p.goto(`http://127.0.0.1:8765/?version=${version}&level=6`,{waitUntil:'networkidle'});
await p.waitForFunction(()=>{
if(!window.Laya?.stage)return false;
function v(n){for(const c of n._components||[])if(c.playerNodes)window.g=c;for(let i=0;i<n.numChildren;i++)v(n.getChildAt(i));}v(Laya.stage);
return window.g?.total>0&&!g.busy;
});
let release;
await p.route('**/resources/levels/level8.json',async r=>{
await new Promise(resolve=>release=resolve);
await r.fulfill({status:200,contentType:'application/json',body:fs.readFileSync('preview/resources/levels/level8.json','utf8')});
});
const original=await p.evaluate(()=>JSON.stringify(g.queues));
await p.evaluate(()=>{window.switchPromise=g.loadLevel(8);});
await p.waitForFunction(()=>g.busy&&g.transitionOverlay?.alpha>.99&&Laya.timer.scale===0);
await p.screenshot({path:`${out}/loading-shield.png`});
await p.mouse.click(50,60);await p.mouse.click(200,890);await p.mouse.click(390,1060);
assert.equal(await p.evaluate(()=>JSON.stringify(g.queues)),original);
assert.equal(await p.evaluate(()=>!!g.modal),false);
release();await p.evaluate(()=>switchPromise);assert.equal(await p.evaluate(()=>g.level),8);
assert(await p.evaluate(()=>!g.busy&&!g.transitionOverlay&&g.carLabelBindings.every(b=>b.label.x!==0||b.label.y!==0)));
await p.evaluate(()=>{
window.alignment={frames:0,maxError:0};
class Probe extends Laya.Script {
onPostRender(){
for(const b of g.carLabelBindings){
if(!b.label.visible||b.node.destroyed)continue;
const w=b.node.transform.position,v=new Laya.Vector4();
g.camera.worldToViewportPoint(new Laya.Vector3(w.x,w.y+1,w.z),v);
alignment.maxError=Math.max(alignment.maxError,Math.abs(v.x-b.label.x),Math.abs(v.y-13-b.label.y));
}alignment.frames++;
}
}
window.probe=g.owner.addComponent(Probe);
});
const pt=await p.evaluate(()=>g.laneHits[0].localToGlobal(new Laya.Point(42,60)));await p.mouse.click(pt.x,pt.y);
await p.waitForTimeout(1200);
const alignment=await p.evaluate(()=>{probe.destroy();return window.alignment;});
assert(alignment.frames>=5);assert(alignment.maxError<1,JSON.stringify(alignment));
const invalid={...JSON.parse(fs.readFileSync('preview/resources/levels/level49.json')),bullet_order:'[]'};
await p.route('**/resources/levels/level49.json',r=>r.fulfill({status:200,contentType:'application/json',body:JSON.stringify(invalid)}));
const preserved=await p.evaluate(async()=>{
const state=()=>JSON.stringify([g.level,g.board,g.queues,g.parked,g.cleared,g.activeMoves,[...g.reservedPassengers],[...g.pendingPassengers.values()]]);
const before=state();await g.loadLevel(49);return before===state();
});
assert(preserved);assert(await p.evaluate(()=>g.modal?.name==='panel_关卡载入失败'&&!g.transitionOverlay&&Laya.timer.scale===0));
await p.waitForTimeout(260);await p.screenshot({path:`${out}/failed-transition.png`});
await p.unroute('**/resources/levels/level49.json');
const retry=await p.evaluate(()=>{const b=g.modal.getChildByName('panel_card').getChildByName('action_重试');return b.localToGlobal(new Laya.Point(b.width/2,b.height/2));});
await p.mouse.click(retry.x,retry.y);await p.waitForFunction(()=>g.level===49&&!g.busy&&!g.modal&&!g.transitionOverlay);
assert.deepEqual(errors,[]);
const result={loadingInputShield:true,labelAlignment:alignment,failedSwitchPreservedState:preserved,retryByActualClick:true,errors};
fs.writeFileSync(`${out}/transition-regression.json`,JSON.stringify(result,null,2));console.log(JSON.stringify(result));
await Promise.race([b.close(),new Promise(r=>setTimeout(r,3000))]);process.exit(0);
})().catch(e=>{console.error(e);process.exit(1)});