Files
elizaos--eliza/scripts/view-audit/web-interactions.mjs
T
wehub-resource-sync 426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

197 lines
6.3 KiB
JavaScript

/**
* Web button/input interaction pass — per view, clicks every enabled, visible,
* NON-destructive button and types into every text input, capturing JS-level
* errors (pageerror / error-boundary) that indicate a REAL interaction bug.
* Network 4xx is ignored (dev-backend config noise); only JS faults count.
*/
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
import { chromium } from "@playwright/test";
import { ROUTES } from "./routes.mjs";
const OUT = "/tmp/view-audit/web-interactions";
mkdirSync(OUT, { recursive: true });
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const DESTRUCTIVE =
/delete|remove|reset|disconnect|sign out|log ?out|clear|wipe|revoke|leave|uninstall|deactivate|danger|forget|erase|drop|unpair|trash|destroy/i;
async function main() {
const browser = await chromium.launch({ headless: true });
const ctx = await browser.newContext({
viewport: { width: 1280, height: 900 },
ignoreHTTPSErrors: true,
});
const page = await ctx.newPage();
await page.addInitScript(() => {
try {
localStorage.setItem(
"elizaos:active-server",
JSON.stringify({
id: "local:dev",
kind: "remote",
label: "Local dev agent",
apiBase: "http://127.0.0.1:31337",
}),
);
localStorage.setItem("eliza:first-run-complete", "1");
} catch {}
});
await page
.goto("http://127.0.0.1:2138", {
waitUntil: "domcontentloaded",
timeout: 30000,
})
.catch(() => {});
await sleep(4000);
const results = [];
for (const [route, slug] of ROUTES) {
const goView = () =>
page
.evaluate((u) => {
history.pushState(null, "", u);
window.dispatchEvent(new PopStateEvent("popstate"));
if (u.includes("#"))
window.dispatchEvent(new HashChangeEvent("hashchange"));
}, route)
.catch(() => {});
await goView();
await sleep(1500);
// enumerate enabled, visible, non-destructive buttons + their labels
let controls = [];
try {
controls = await page.evaluate((destrSrc) => {
const DESTR = new RegExp(destrSrc, "i");
const lbl = (e) =>
(
e.getAttribute("aria-label") ||
e.getAttribute("title") ||
e.textContent ||
""
)
.replace(/\s+/g, " ")
.trim();
return [...document.querySelectorAll('button,[role="button"]')]
.map((e, i) => ({
i,
l: lbl(e),
dis: !!e.disabled || e.getAttribute("aria-disabled") === "true",
vis: !!(e.offsetParent || e.getClientRects().length),
}))
.filter((c) => c.vis && !c.dis && c.l && !DESTR.test(c.l));
}, DESTRUCTIVE.source);
} catch {}
const issues = [];
const max = Math.min(controls.length, 18);
for (let k = 0; k < max; k++) {
const pageErrors = [];
const onErr = (e) =>
pageErrors.push(String(e.message || e).slice(0, 200));
page.on("pageerror", onErr);
// re-resolve the button each time (DOM may have changed); match by label
const target = controls[k];
let clicked = false;
try {
const handle = await page.evaluateHandle((lab) => {
const lbl = (e) =>
(
e.getAttribute("aria-label") ||
e.getAttribute("title") ||
e.textContent ||
""
)
.replace(/\s+/g, " ")
.trim();
return [...document.querySelectorAll('button,[role="button"]')].find(
(e) =>
lbl(e) === lab &&
(e.offsetParent || e.getClientRects().length) &&
!e.disabled,
);
}, target.l);
const el = handle.asElement();
if (el) {
await el.click({ timeout: 1500 }).catch(() => {});
clicked = true;
}
} catch {}
await sleep(550);
// detect error boundary
let errBoundary = false;
try {
errBoundary = await page.evaluate(() =>
/something went wrong|failed to load|is not a function|cannot read prop/i.test(
document.body.innerText || "",
),
);
} catch {}
page.off("pageerror", onErr);
if (clicked && (pageErrors.length || errBoundary)) {
issues.push({
button: target.l.slice(0, 40),
jsError: pageErrors[0] || null,
errorBoundary: errBoundary,
});
}
// recover: if path moved or a dialog/overlay opened, go back to the view + dismiss
await page.keyboard.press("Escape").catch(() => {});
const onView = await page
.evaluate(
(r) =>
location.pathname + location.hash === r ||
location.pathname === r.split("#")[0],
route,
)
.catch(() => true);
if (!onView) {
await goView();
await sleep(900);
}
}
// type into the first few text inputs (smoke: does typing throw?)
const inputIssues = [];
try {
const inputs = await page.$$(
'input[type="text"],input[type="search"],input:not([type]),textarea',
);
for (const inp of inputs.slice(0, 5)) {
const pe = [];
const onErr = (e) => pe.push(String(e.message).slice(0, 150));
page.on("pageerror", onErr);
await inp.fill("audit-test").catch(() => {});
await sleep(200);
page.off("pageerror", onErr);
if (pe.length) inputIssues.push(pe[0]);
}
} catch {}
const f = {
slug,
route,
buttonsTested: max,
buttonsAvailable: controls.length,
buttonIssues: issues,
inputIssues,
};
results.push(f);
const line = `${slug.padEnd(22)} tested ${String(max).padStart(2)}/${String(controls.length).padStart(2)} btns issues=${issues.length} inputErr=${inputIssues.length}${issues.length ? " -> " + issues.map((x) => x.button).join("|") : ""}`;
console.error(line);
appendFileSync(`${OUT}/progress.log`, line + "\n");
writeFileSync(
`${OUT}/report-web-interactions.json`,
JSON.stringify({ count: results.length, results }, null, 2),
);
}
await browser.close();
console.error("DONE");
}
main().then(
() => process.exit(0),
(e) => {
console.error("ERR:", e.message);
process.exit(1);
},
);