2026-10-06 02:02:08 +00:00
|
|
|
// Uses an already authenticated agent-browser session. Run again after signing in
|
|
|
|
|
// to screenshot every dashboard route. Evidence defaults to a temporary directory.
|
|
|
|
|
import { execFileSync } from "node:child_process";
|
|
|
|
|
import { mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
|
|
|
import { join } from "node:path";
|
|
|
|
|
|
|
|
|
|
const session = process.env.MEGA_BROWSER_SESSION || "mega-login";
|
|
|
|
|
const output = process.env.MEGA_AUDIT_DIR || "/tmp/mega-auth-mobile";
|
|
|
|
|
mkdirSync(output, { recursive: true });
|
|
|
|
|
const browser = (...args) => execFileSync("agent-browser", ["--session", session, ...args], { encoding: "utf8", timeout: 45000 });
|
|
|
|
|
const routes = process.argv.slice(2);
|
|
|
|
|
if (!routes.length) throw new Error("Pass one or more route paths to audit.");
|
|
|
|
|
const evaluate = `(() => {
|
|
|
|
|
const shown = (el) => { const r=el.getBoundingClientRect(),s=getComputedStyle(el);return r.width>0&&r.height>0&&r.right>0&&r.left<innerWidth&&r.bottom>0&&s.visibility!=='hidden'&&s.display!=='none'; };
|
|
|
|
|
const taps=[...document.querySelectorAll('button,a,input,select,textarea,summary,[role="button"]')].filter(shown);
|
|
|
|
|
const smallTaps=taps.filter(el=>{const r=el.getBoundingClientRect();return r.width<43.99||r.height<43.99}).map(el=>({tag:el.tagName,label:(el.getAttribute('aria-label')||el.textContent||el.getAttribute('placeholder')||el.id).trim().slice(0,70),width:el.getBoundingClientRect().width,height:el.getBoundingClientRect().height}));
|
|
|
|
|
const walker=document.createTreeWalker(document.body,4);const smallText=[];let node;
|
|
|
|
|
while(node=walker.nextNode()){const el=node.parentElement;if(el&&!['SCRIPT','STYLE','OPTION'].includes(el.tagName)&&node.textContent.trim()&&shown(el)&&parseFloat(getComputedStyle(el).fontSize)<11.99)smallText.push({text:node.textContent.trim().slice(0,70),size:getComputedStyle(el).fontSize});}
|
|
|
|
|
const wideTables=[...document.querySelectorAll('table')].filter(shown).filter(table=>table.scrollWidth>table.parentElement.clientWidth).map(table=>({contained:['auto','scroll'].includes(getComputedStyle(table.parentElement).overflowX),stickyFirstColumn:getComputedStyle(table.querySelector('tbody tr td')).position==='sticky',touchScroll:/-webkit-overflow-scrolling:\\s*touch/.test(table.parentElement.getAttribute('style')||''),scrollAffordance:!!table.parentElement.parentElement.querySelector('[aria-hidden="true"]')}));
|
|
|
|
|
return {route:location.pathname,width:innerWidth,height:innerHeight,overflow:document.documentElement.scrollWidth>document.documentElement.clientWidth,tapCount:taps.length,smallTaps,smallText,wideTables};
|
|
|
|
|
})()`;
|
|
|
|
|
const file = join(output, "results.json");
|
|
|
|
|
const results = existsSync(file) ? JSON.parse(readFileSync(file, "utf8")) : [];
|
|
|
|
|
for (const [width, height] of [[375, 812], [768, 1024]]) {
|
|
|
|
|
browser("set", "viewport", String(width), String(height));
|
|
|
|
|
for (const route of routes) {
|
|
|
|
|
browser("open", `http://localhost:6007${route}`);
|
|
|
|
|
browser("wait", "--load", "networkidle");
|
2026-10-21 04:39:15 +00:00
|
|
|
if (route === "/clock") browser("wait", "--fn", "!!document.querySelector('.plant-clock-page') && !document.querySelector('.plant-clock-page').textContent.includes('--:--')");
|
2026-10-06 02:02:08 +00:00
|
|
|
if (route === "/plant/map") browser("wait", "--fn", "!!document.querySelector('.leaflet-container')");
|
|
|
|
|
const raw = execFileSync("agent-browser", ["--session", session, "eval", "--stdin"], { input: evaluate, encoding: "utf8", timeout: 45000 });
|
|
|
|
|
const result = JSON.parse(raw);
|
|
|
|
|
const filename = `${route === "/" ? "dashboard" : route.slice(1).replaceAll("/", "-")}-${width}.png`;
|
|
|
|
|
browser("screenshot", "--full", join(output, filename));
|
|
|
|
|
const index = results.findIndex((entry) => entry.route === result.route && entry.width === width);
|
|
|
|
|
if (index >= 0) results[index] = result; else results.push(result);
|
|
|
|
|
writeFileSync(file, JSON.stringify(results, null, 2));
|
|
|
|
|
console.log(JSON.stringify({ requestedRoute: route, ...result }));
|
|
|
|
|
}
|
|
|
|
|
}
|