Files
FreeCADWasmDev/tools/run_p6_robot_chrome.mjs
wangdequan 9a7c4f0335 Save FreeCAD Web workspace sources and user validation evidence
IFC native binding remains incomplete; preserve accurate test results.
2026-09-15 22:01:58 +08:00

2 lines
4.7 KiB
JavaScript

import crypto from"node:crypto";import fs from"node:fs";import http from"node:http";import os from"node:os";import path from"node:path";import{spawn}from"node:child_process";const root=path.resolve(path.dirname(new URL(import.meta.url).pathname),".."),dist=path.join(root,"web/dist"),docs=path.join(root,"docs/p6"),chromePath="/usr/bin/google-chrome",viewport={width:1440,height:900,deviceScaleFactor:1},wait=ms=>new Promise(r=>setTimeout(r,ms)),hash=v=>crypto.createHash("sha256").update(v).digest("hex"),server=http.createServer((q,res)=>{const rel=new URL(q.url??"/","http://x").pathname.replace(/^\//,"")||"robot-create.html",file=path.resolve(dist,rel);if(!fs.existsSync(file)){res.writeHead(404);return res.end()}res.writeHead(200,{"content-type":path.extname(file)===".html"?"text/html":path.extname(file)===".js"?"text/javascript":"application/wasm"});fs.createReadStream(file).pipe(res)});await new Promise(r=>server.listen(0,"127.0.0.1",r));const origin=`http://127.0.0.1:${server.address().port}`,profile=fs.mkdtempSync(path.join(os.tmpdir(),"robot-chrome-")),chrome=spawn(chromePath,["--headless=new","--no-sandbox","--disable-gpu","--disable-dev-shm-usage","--remote-debugging-port=0","--remote-allow-origins=*",`--user-data-dir=${profile}`,`--window-size=${viewport.width},${viewport.height}`,`${origin}/robot-create.html`],{stdio:["ignore","pipe","pipe"]});let out="";chrome.stdout.on("data",x=>out+=x);chrome.stderr.on("data",x=>out+=x);class C{constructor(u){this.s=new WebSocket(u);this.i=0;this.p=new Map;this.l=new Map;this.ready=new Promise((r,j)=>{this.s.addEventListener("open",r,{once:true});this.s.addEventListener("error",j,{once:true})});this.s.addEventListener("message",e=>{const m=JSON.parse(e.data);if(m.id){const p=this.p.get(m.id);if(p){this.p.delete(m.id);m.error?p.reject(Error(m.error.message)):p.resolve(m.result)}}else for(const f of this.l.get(m.method)||[])f(m.params)})}async send(m,p={}){await this.ready;return new Promise((r,j)=>{const i=++this.i;this.p.set(i,{resolve:r,reject:j});this.s.send(JSON.stringify({id:i,method:m,params:p}))})}on(m,f){this.l.set(m,[...(this.l.get(m)||[]),f])}close(){this.s.close()}}let c;try{let t;for(let i=0;i<300&&!t;i++){const m=out.match(/DevTools listening on ws:\/\/([^\s]+)/);if(m){const port=m[1].split(":")[1].split("/")[0];try{t=(await fetch(`http://127.0.0.1:${port}/json/list`).then(r=>r.json())).find(x=>x.type==="page")}catch{}}if(!t)await wait(100)}c=new C(t.webSocketDebuggerUrl);const req=[],ex=[];c.on("Network.requestWillBeSent",e=>req.push(e.request.url));c.on("Runtime.exceptionThrown",e=>ex.push(e.exceptionDetails));await c.send("Runtime.enable");await c.send("Network.enable");await c.send("Emulation.setDeviceMetricsOverride",{...viewport,mobile:false});const ev=x=>(c.send("Runtime.evaluate",{expression:x,returnByValue:true,awaitPromise:true})).then(x=>x.result?.value),state=()=>ev("window.__freecadRobotCreateState"),stage=async x=>{for(let i=0;i<300;i++){const v=await state();if(v?.stage===x)return v;await wait(100)}throw Error(x)},ready=await stage("ready");await ev("document.querySelector('#run').click()");const success=await stage("success");await ev("document.querySelector('#error').click()");const expected=await stage("expected-error");await ev("document.querySelector('#reset').click()");const reset=await stage("reset"),shot=Buffer.from((await c.send("Page.captureScreenshot",{format:"png",fromSurface:true})).data,"base64");fs.writeFileSync(path.join(docs,"robot-chrome.png"),shot);const external=req.filter(x=>!x.startsWith(origin)),version=await new Promise(r=>{const p=spawn(chromePath,["--version"],{stdio:["ignore","pipe","ignore"]});let t="";p.stdout.on("data",x=>t+=x);p.on("close",()=>r(t.trim()))}),result=success?.response?.kind==="result"&&success.response.result?.objectType==="Robot::RobotObject"&&expected?.response?.error?.code==="invalid_fixture"&&reset?.stage==="reset"&&!external.length&&!ex.length?"pass":"fail",report={schema:"freecad-web-p6-robot-chrome/v1",taskId:"FW-P6-ROB-F",module:"Robot",action:"create/recompute",result,chrome:{path:chromePath,version},mode:"headless-new",origin,viewport,cases:{ready,success,expected,reset},network:{requests:req,externalRequests:external,externalRequestCount:external.length},exceptions:ex,evidence:{screenshot:"docs/p6/robot-chrome.png",screenshotSha256:hash(shot)}};fs.writeFileSync(path.join(docs,"robot-chrome.json"),JSON.stringify(report,null,2)+"\n");fs.writeFileSync(path.join(root,"docs/freecad-p6-robot-f-chrome.log"),JSON.stringify({result,chrome:version,externalRequests:external.length,exceptions:ex.length})+"\n");console.log(JSON.stringify({result,chrome:version,externalRequests:external.length,exceptions:ex.length}));if(result!=="pass")process.exitCode=1}finally{c?.close();chrome.kill("SIGTERM");server.close()}