import assert from "node:assert/strict"; import { readFile, writeFile, mkdir } from "node:fs/promises"; import { pathToFileURL } from "node:url"; const { chromium } = await import(pathToFileURL(process.env.PLAYWRIGHT_MODULE).href); const root = new URL("../", import.meta.url); const settings = JSON.parse(await readFile(new URL("web/workspace/web-settings.json", root), "utf8")); const catalog = JSON.parse(await readFile(new URL("web/workspace/machine-catalog.json", root), "utf8")); settings.ui.jog_increments = ["0.25", "2.5"]; settings.viewer.colors.background = "#550055"; settings.viewer.colors.path = "#00ff00"; settings.assets.axis_images = "configured-icons"; catalog.default_machine = "xyzbc-trt"; const sourceIni = catalog.machines[1].entrypoint; catalog.machines[1].entrypoint = sourceIni.replace(/[^/]+$/, "configured-machine.ini"); catalog.machines[1].program = catalog.machines[1].program.replace(/[^/]+$/, "configured-preview.ngc"); const browser = await chromium.launch({ executablePath: process.env.BROWSER_EXECUTABLE_PATH ?? "/usr/bin/google-chrome", headless: true, args: ["--no-sandbox", "--use-angle=swiftshader", "--enable-unsafe-swiftshader"] }); const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } }); const errors = []; page.on("pageerror", (error) => errors.push(error.message)); const configRequests = [], iconRequests = []; await page.route(/web-settings\.json\?import|machine-catalog\.json\?import/, async (route) => { configRequests.push(route.request().url()); const value = route.request().url().includes("web-settings.json") ? settings : catalog; await route.fulfill({ contentType: "text/javascript", body: `export default ${JSON.stringify(value)};` }); }); await page.route("**/configured-icons/*", async (route) => { iconRequests.push(route.request().url()); const name = new URL(route.request().url()).pathname.split("/").at(-1); const response = await route.fetch({ url: new URL(`/linuxcnc/share/axis/images/${name}`, route.request().url()).href }); await route.fulfill({ response }); }); await page.route("**/configured-machine.ini", async (route) => { const response = await route.fetch({ url: new URL(`/${sourceIni}`, route.request().url()).href }); const content = (await response.text()).replace(/MAX_FEED_OVERRIDE\s*=\s*[^\r\n]+/, "MAX_FEED_OVERRIDE = 1.3"); await route.fulfill({ response, body: content }); }); await page.route("**/configured-preview.ngc", (route) => route.fulfill({ contentType: "text/plain", body: "G21 G90\nG0 X-30 Y-30 Z20\nG1 X30 Y-30 F600\nY30\nX-30\nY-30\nM2\n" })); try { await page.goto(process.env.XYZAC_PANEL_URL ?? "http://127.0.0.1:5174/"); await page.waitForFunction(() => document.querySelector("main")?.dataset.axisProgramLoading === "false"); assert.equal(await page.locator(".xyzac-machine-view").getAttribute("data-xyzac-model-id"), "xyzbc-trt"); assert.equal(await page.locator("main").getAttribute("data-axis-program-path"), catalog.machines[1].program); assert.deepEqual(await page.locator('[data-axis-control="manual-jog-increment"] option').allTextContents(), ["Continuous", "0.25", "2.5"]); assert.equal(await page.locator('[data-axis-control="override-feed"]').getAttribute("max"), "130"); assert.ok(configRequests.length >= 2); assert.ok(iconRequests.length > 10); const colors = await page.locator('.xyzac-machine-view canvas[data-engine]').evaluate(async (canvas) => { await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))); const gl = canvas.getContext("webgl2"); const data = new Uint8Array(canvas.width * canvas.height * 4); gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, data); let purple = 0, green = 0; for (let i = 0; i < data.length; i += 4) { if (data[i] > 30 && data[i + 2] > 30 && data[i + 1] < 10) purple++; if (data[i + 1] > 150 && data[i + 1] > data[i] + 30 && data[i + 1] > data[i + 2] + 30) green++; } return { purple, green }; }); assert.ok(colors.purple > 1000, JSON.stringify(colors)); await page.screenshot({ path: new URL("coverage/configuration/configured-ui.png", root).pathname, fullPage: true }); assert.ok(colors.green > 0, JSON.stringify(colors)); assert.deepEqual(errors, []); const output = new URL("coverage/configuration/", root); await mkdir(output, { recursive: true }); await page.screenshot({ path: new URL("configured-ui.png", output).pathname, fullPage: true }); const report = { report_id: "configurable-ui-browser-v1", status: "verified", default_machine: catalog.default_machine, entrypoint: catalog.machines[1].entrypoint, program: catalog.machines[1].program, increments: settings.ui.jog_increments, feed_max: 1.3, colors, configured_icons: iconRequests.length, browser_errors: errors }; await writeFile(new URL("browser.report.json", output), JSON.stringify(report, null, 2) + "\n"); console.log(JSON.stringify(report)); } finally { await browser.close(); }