102 lines
6.6 KiB
JavaScript
102 lines
6.6 KiB
JavaScript
import assert from "node:assert/strict";
|
||
import { pathToFileURL } from "node:url";
|
||
|
||
const modulePath = process.env.PLAYWRIGHT_MODULE;
|
||
if (!modulePath) {
|
||
console.log(JSON.stringify({ schema: "rtcp-program-library-browser.v1", status: "blocked", code: "playwright_unavailable" }));
|
||
process.exit(0);
|
||
}
|
||
const imported = await import(pathToFileURL(modulePath).href);
|
||
const playwright = imported.default ?? imported;
|
||
const browser = await playwright.chromium.launch({ headless: true, executablePath: process.env.BROWSER_EXECUTABLE_PATH ?? "/usr/bin/google-chrome", 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(`pageerror:${error.message}`));
|
||
page.on("console", (message) => { if (message.type() === "error") errors.push(`console:${message.text()}`); });
|
||
const control = (id) => page.locator(`[data-axis-control="${id}"]`);
|
||
const main = page.locator("main.axis-app");
|
||
const waitReady = () => page.waitForFunction(() => {
|
||
const element = document.querySelector("main.axis-app");
|
||
return element?.dataset.axisStateSource === "linuxcnc-wasm" && element?.dataset.axisProgramLoading === "false";
|
||
}, null, { timeout: 45000 });
|
||
const waitPath = (path) => page.waitForFunction((expected) => {
|
||
const element = document.querySelector("main.axis-app");
|
||
return element?.dataset.axisProgramLoading === "false" && element?.dataset.axisProgramPath === expected && element?.dataset.axisProgramStatus === "loaded";
|
||
}, path, { timeout: 45000 });
|
||
|
||
const report = { schema: "rtcp-program-library-browser.v1", status: "running", checks: [] };
|
||
try {
|
||
await page.goto(process.env.RTCP_LIBRARY_URL ?? "http://127.0.0.1:5174/", { waitUntil: "domcontentloaded", timeout: 30000 });
|
||
await waitReady();
|
||
assert.match(await control("workspace-rtcp-programs").textContent(), /AC(XYZAC) RTCP 程序 \(15\)/);
|
||
await control("workspace-rtcp-programs").click();
|
||
assert.equal(await control("rtcp-program-scope").inputValue(), "machine");
|
||
assert.equal(await control("rtcp-program-select").locator("option").count(), 15);
|
||
assert.deepEqual(await control("rtcp-program-select").locator("optgroup").evaluateAll((nodes) => nodes.map((node) => ({ label: node.label, count: node.querySelectorAll("option").length }))), [
|
||
{ label: "TRT 旋转倾斜台 XYZAC/XYZBC (12)", count: 12 },
|
||
{ label: "gmoccapy TRT 示例 (3)", count: 3 },
|
||
]);
|
||
report.checks.push("AC machine defaults to 15 XYZAC/common programs");
|
||
|
||
await control("rtcp-program-scope").selectOption("all");
|
||
assert.equal(await control("rtcp-program-select").locator("option").count(), 32);
|
||
assert.deepEqual(await control("rtcp-program-select").locator("optgroup").evaluateAll((nodes) => nodes.map((node) => ({ label: node.label, count: node.querySelectorAll("option").length }))), [
|
||
{ label: "TRT 旋转倾斜台 XYZAC/XYZBC (16)", count: 16 },
|
||
{ label: "gmoccapy TRT 示例 (5)", count: 5 },
|
||
{ label: "XYZAB 双旋转台 (3)", count: 3 },
|
||
{ label: "Bridge Mill / 5axiskins (4)", count: 4 },
|
||
{ label: "Hexapod genhexkins RTCP (3)", count: 3 },
|
||
{ label: "独立五轴动态刀长示例 (1)", count: 1 },
|
||
]);
|
||
report.checks.push("all-source scope retains 32 programs and 6 source groups");
|
||
|
||
await control("rtcp-program-select").selectOption("trt-remap-m428");
|
||
assert.equal(await control("rtcp-program-load").isDisabled(), true);
|
||
await control("rtcp-program-source").click();
|
||
await page.waitForFunction(() => document.querySelector(".rtcp-program-source")?.value.includes("motion.switchkins-type"));
|
||
report.checks.push("support remap is source-readable and execution-disabled");
|
||
|
||
await control("rtcp-program-select").selectOption("xyzab-tdr-demo");
|
||
assert.equal(await control("rtcp-program-load").isDisabled(), true);
|
||
assert.match(await page.locator(".rtcp-incompatible").textContent(), /需要 XYZAB/);
|
||
report.checks.push("mismatched XYZAB program is visible and execution-disabled");
|
||
|
||
const xyzacPath = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins_test_3.ngc";
|
||
await control("rtcp-program-select").selectOption("trt-xyzac-switchkins-test-3");
|
||
assert.equal(await control("rtcp-program-load").isDisabled(), false);
|
||
await control("rtcp-program-load").click();
|
||
await waitPath(xyzacPath);
|
||
assert.match(await main.getAttribute("data-axis-program-source-hash"), /^fnv1a64:/);
|
||
assert.ok(Number(await page.locator(".xyzac-tool-preview").getAttribute("data-xyzac-toolpath-count")) > 0);
|
||
report.checks.push("XYZAC switchkins test loaded through WASM with canonical source hash and preview");
|
||
|
||
await page.keyboard.press("Escape");
|
||
const xyzbcCase = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini";
|
||
await control("workspace-case-select").selectOption(xyzbcCase);
|
||
await page.waitForFunction((expected) => document.querySelector('[data-axis-control="workspace-case-select"]')?.value === expected && document.querySelector("main.axis-app")?.dataset.axisProgramLoading === "false", xyzbcCase, { timeout: 45000 });
|
||
assert.match(await control("workspace-rtcp-programs").textContent(), /BC(XYZBC) RTCP 程序 \(9\)/);
|
||
await control("workspace-rtcp-programs").click();
|
||
assert.equal(await control("rtcp-program-scope").inputValue(), "machine");
|
||
assert.equal(await control("rtcp-program-select").locator("option").count(), 9);
|
||
const xyzbcPath = "linuxcnc/configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzbc.ngc";
|
||
await control("rtcp-program-select").selectOption("gmoccapy-boat-xyzbc");
|
||
assert.equal(await control("rtcp-program-load").isDisabled(), false);
|
||
await control("rtcp-program-load").click();
|
||
await waitPath(xyzbcPath);
|
||
assert.ok(Number(await page.locator(".xyzac-tool-preview").getAttribute("data-xyzac-toolpath-count")) > 0);
|
||
report.checks.push("BC machine defaults to 9 XYZBC/common programs and loads gmoccapy XYZBC through WASM");
|
||
|
||
assert.deepEqual(errors, []);
|
||
report.status = "verified";
|
||
} catch (error) {
|
||
report.status = "failed";
|
||
report.error = error instanceof Error ? error.stack : String(error);
|
||
await page.screenshot({ path: "/tmp/rtcp-program-library-failure.png", fullPage: true });
|
||
throw error;
|
||
} finally {
|
||
report.browser_errors = errors;
|
||
report.final = await main.evaluate((element) => ({ coordinates: element.querySelector('[data-axis-control="workspace-case-select"]')?.value, program_path: element.dataset.axisProgramPath, program_status: element.dataset.axisProgramStatus, source_hash: element.dataset.axisProgramSourceHash })).catch(() => null);
|
||
await browser.close();
|
||
}
|
||
console.log(JSON.stringify(report));
|