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();