Move panel temporary state to worker OPFS

This commit is contained in:
2026-09-03 14:03:32 +08:00
parent 0f6691a9fc
commit 2b1e249baf
22 changed files with 688 additions and 42 deletions

View File

@@ -0,0 +1,39 @@
import { pathToFileURL } from "node:url";
const modulePath = process.env.PLAYWRIGHT_MODULE;
const url = process.env.XYZAC_PANEL_URL ?? "https://49.232.250.35:58010/";
if (!modulePath) { console.log(JSON.stringify({ schema: "xyzac-opfs-browser.v1", status: "blocked", code: "playwright_unavailable" })); process.exit(0); }
const imported = await import(pathToFileURL(modulePath).href);
const playwright = imported.default ?? imported;
const userDataDir = process.env.XYZAC_OPFS_PROFILE ?? "/tmp/xyzac-opfs-browser-profile";
const context = await playwright.chromium.launchPersistentContext(userDataDir, { headless: true, executablePath: process.env.BROWSER_EXECUTABLE_PATH ?? "/usr/bin/google-chrome", ignoreHTTPSErrors: true, viewport: { width: 1440, height: 1000 }, args: ["--ignore-certificate-errors", "--disable-gpu"] });
const page = context.pages()[0] ?? await context.newPage();
const errors = [];
page.on("pageerror", (error) => errors.push(`pageerror:${error.message}`));
page.on("console", (message) => { if (message.type() === "error") errors.push(`console:${message.text()}`); });
const main = page.locator("main.axis-app");
const waitReady = async () => { for (let index = 0; index < 300; index += 1) { if (await main.count() && await main.getAttribute("data-axis-state-source") === "linuxcnc-wasm") return; await page.waitForTimeout(250); } throw new Error("wasm_startup_timeout"); };
const opfsFiles = async () => page.evaluate(async () => {
const root = await navigator.storage.getDirectory();
const names = [];
const walk = async (directory, prefix) => {
for await (const [name, handle] of directory.entries()) { names.push(`${prefix}/${name}`); if (handle.kind === "directory") await walk(handle, `${prefix}/${name}`); }
};
await walk(root, "");
return names.sort();
});
await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30000 });
await waitReady();
await main.locator('button[aria-label="Top view"]').click();
await main.getByRole("tab", { name: "DRO", exact: true }).click();
await main.locator('input[value="a"]').check();
await page.waitForTimeout(1800);
const before = await main.evaluate((element) => ({ view: element.dataset.axisView, zoom: element.dataset.axisZoom, cache: element.dataset.axisUiCache, tab: element.querySelector('#axis-tab-dro')?.getAttribute("aria-selected") === "true" ? "DRO" : "Manual", localStorageKeys: Object.keys(localStorage) }));
const files = await opfsFiles();
await page.reload({ waitUntil: "domcontentloaded", timeout: 30000 });
await waitReady();
await page.waitForTimeout(500);
const after = await main.evaluate((element) => ({ view: element.dataset.axisView, zoom: element.dataset.axisZoom, cache: element.dataset.axisUiCache, dro: element.querySelector('#axis-tab-dro')?.getAttribute("aria-selected"), axisA: element.querySelector('input[value="a"]')?.checked, localStorageKeys: Object.keys(localStorage) }));
await page.screenshot({ path: "/tmp/xyzac-opfs-browser.png", fullPage: true });
console.log(JSON.stringify({ schema: "xyzac-opfs-browser.v1", status: errors.length === 0 && before.cache === "opfs" && after.cache === "opfs" && before.view === after.view && before.tab === "DRO" && after.dro === "true" && after.axisA === true && before.localStorageKeys.length === 0 && after.localStorageKeys.length === 0 && files.some((file) => file.includes("state-")) ? "verified" : "failed", url, before, after, opfs_files: files, browser_errors: errors, screenshot: "/tmp/xyzac-opfs-browser.png" }));
await context.close();