Organize/.cdp/verify-switch2.cjs

80 lines
5.8 KiB
JavaScript

/* Verify every theme switch updates group card colors in the UI (no reload).
* Robust menu clicks: verify the dropdown is open, retry, log what's in it. */
const { spawn } = require("node:child_process");
const fs = require("node:fs");
const http = require("node:http");
const EDGE = "C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe";
const PORT = 9362;
function httpJson(p){return new Promise((res,rej)=>{const r=http.request({host:"127.0.0.1",port:PORT,path:p},(x)=>{let d="";x.on("data",c=>d+=c);x.on("end",()=>{try{res(JSON.parse(d))}catch{res(d)}})});r.on("error",rej);r.end()})}
(async()=>{
const prof=fs.mkdtempSync(require("node:os").tmpdir()+"/edge-prof-");
const proc=spawn(EDGE,["--headless=new","--remote-debugging-port="+PORT,"--user-data-dir="+prof,"--no-first-run","--no-default-browser-check","--disable-gpu","about:blank"],{stdio:"ignore"});
let tg=null;for(let i=0;i<40;i++){await new Promise(r=>setTimeout(r,500));try{tg=await httpJson("/json/list");break}catch{}}
const page=tg.find(t=>t.type==="page");
const ws=new WebSocket(page.webSocketDebuggerUrl);
await new Promise(r=>ws.onopen=r);let id=0;
const send=(m,p={})=>new Promise(res=>{const i2=++id;ws.send(JSON.stringify({id:i2,method:m,params:p}));const t=setInterval(()=>{const f=ws._buf.find(b=>b.id===i2);if(f){clearInterval(t);res(f.result??f.error)}},10)});
ws._buf=[];const errs=[];
ws.onmessage=(m)=>{const j=JSON.parse(m.data);if(j.id){ws._buf.push(j);return;}
if(j.method==="Runtime.consoleAPICalled"&&j.params.type==="error"){errs.push(j.params.args.map(a=>(a.value??a.description??"")).join(" ").slice(0,120));}
if(j.method==="Runtime.exceptionThrown"){errs.push("EXC: "+(j.params.exceptionDetails?.exception?.description||"").slice(0,120));}};
await send("Runtime.enable");await send("Page.enable");await send("Network.enable");
const cv=fs.readFileSync(process.argv[1],"utf8").split("\n").map(l=>l.trim()).find(l=>l.includes("authjs.session-token")).split("\t").pop();
await send("Network.setCookies",{cookies:[{name:"authjs.session-token",value:cv,domain:"localhost",path:"/",httpOnly:true,sameSite:"Lax"}]});
await send("Emulation.setDeviceMetricsOverride",{width:1440,height:900,deviceScaleFactor:1});
await send("Page.navigate",{url:"http://localhost:3210/"});
await new Promise(r=>setTimeout(r,10000));
const diag=async()=>{const t=await ev("document.title+\" | \"+document.body.innerText.length");const b=await ev("Array.from(document.querySelectorAll(\"button\")).length");console.log("PAGE",t,"buttons:",b);};
const ev=(expr)=>send("Runtime.evaluate",{expression:expr,returnByValue:true,awaitPromise:true}).then(r=>{try{return JSON.stringify(r.result?.value)}catch{return "ERR"}});
const click=async(x,y)=>{await send("Input.dispatchMouseEvent",{type:"mousePressed",x,y,button:"left",clickCount:1});await send("Input.dispatchMouseEvent",{type:"mouseReleased",x,y,button:"left",clickCount:1});};
const sleep=(ms)=>new Promise(r=>setTimeout(r,ms));
const cardState=async()=>JSON.parse(await ev(`(()=>{
const cs=Array.from(document.querySelectorAll("[class*=rounded-2xl]")).filter(c=>/background-color/.test(c.getAttribute("style")||""));
if(!cs.length) return {cards:0};
const s=cs[0].getAttribute("style");
return {cards:cs.length, bg:(s.match(/background-color:([^;]+)/)||[])[1], border:(s.match(/border-color:([^;]+)/)||[])[1], html:document.documentElement.className.split(" ").filter(Boolean).slice(-1).join(",")};
})()`)||"null");
const themeBtnPos=async()=>JSON.parse(await ev(`(()=>{const b=Array.from(document.querySelectorAll("button")).find(x=>(x.textContent||"").trim()==="Theme");if(!b)return null;const r=b.getBoundingClientRect();return {x:Math.round(r.x+r.width/2),y:Math.round(r.y+r.height/2)}})()`)||"null");
const menuButtons=async()=>await ev(`(()=>{const m=document.querySelector('[data-slot="dropdown-menu-content"]');if(!m)return null;return Array.from(m.querySelectorAll("button")).map(b=>(b.textContent||"").trim())})()`)||"null";
async function selectTheme(name){
for(let attempt=1; attempt<=4; attempt++){
const tp=await themeBtnPos();
if(!tp){console.log(` ! Theme btn not found (attempt ${attempt})`);await sleep(500);continue;}
await click(tp.x,tp.y); await sleep(700);
let mb=await menuButtons();
if(!mb||mb==="null"){
console.log(` attempt ${attempt}: menu did not open`);
// toggle closed? open again
await sleep(300); continue;
}
const list=JSON.parse(mb);
const idx=list.findIndex(t=>t===name);
if(idx===-1){console.log(` attempt ${attempt}: menu open, options=${JSON.stringify(list)}, no ${name}`);await sleep(300);continue;}
const pos=JSON.parse(await ev(`(()=>{const m=document.querySelector('[data-slot="dropdown-menu-content"]');const b=Array.from(m.querySelectorAll("button"))[${idx}];const r=b.getBoundingClientRect();return {x:Math.round(r.x+12),y:Math.round(r.y+r.height/2)}})()`));
await click(pos.x,pos.y); await sleep(900);
return true;
}
return false;
}
await diag();
const seq=["Sunset","Default","Ocean","Dark","Sunset"];
const results={};
results["A"]=await cardState();
let letter=65;
for(const name of seq){
console.log(`selecting ${name}:`, await selectTheme(name)?"ok":"FAILED");
results[String.fromCharCode(++letter)]=await cardState();
}
for(const k of Object.keys(results)) console.log(k, results[k]);
const d=(a,b)=>a&&b&&a.cards>0&&a.bg!==b.bg;
console.log("FILL CHANGES: A->B:",d(results.A,results.B),"| B->C:",d(results.B,results.C),"| C->D:",d(results.C,results.D),"| D->E:",d(results.D,results.E),"| E->F:",d(results.E,results.F));
const real=errs.filter(e=>!/favicon|manifest|icon|404/i.test(e));
console.log("ERRORS:", real.length); real.slice(0,4).forEach(e=>console.log(" -",e));
proc.kill();process.exit(0);
})().catch(e=>{console.error("FAIL",e.message);process.exit(1)});