Align AXIS Web control lifecycle with LinuxCNC

This commit is contained in:
2026-09-07 13:47:10 +08:00
parent 35adac6471
commit 0a679b5eeb
34 changed files with 2598 additions and 177 deletions

View File

@@ -75,7 +75,6 @@ try {
}));
results.push(await record("POWER ON", async () => { await click(main.locator('button[aria-label="Power on [F2]"]'), "power_on"); await wait((s) => s.machine === "Machine ON", "power_state"); }));
results.push(await record("HOME ALL", async () => { await click(main.locator('button[aria-label="Home all axes"]'), "home_all"); await wait((s) => s.homed === "[true,true,true,true,true]", "home_state"); }));
results.push(await record("RESET", async () => { await click(main.locator('button[aria-label="Reset machine"]'), "reset"); await wait((s) => s.command?.includes("RESET acknowledged") || s.error === null, "reset_state"); }));
results.push(await record("KINEMATICS IDENTITY/TCP/USERK", async () => {
for (const name of ["Identity", "TCP: XYZAC", "User K", "Identity", "TCP: XYZAC", "User K"]) {
await click(main.getByRole("button", { name: new RegExp(`^${name}`) }), `kinematics_${name}`);
@@ -99,13 +98,13 @@ try {
await axisInput.check();
await click(main.locator(`button[aria-label="Jog ${axis} positive"]`), `jog_${axis}_positive`);
await wait((s) => s.error === null, `jog_${axis}_ack`);
await click(main.getByRole("button", { name: "Stop", exact: true }).first(), `jog_${axis}_stop`);
await click(main.getByRole("button", { name: "Stop jog", exact: true }), `jog_${axis}_stop`);
}
}));
results.push(await record("SPINDLE", async () => {
await click(main.locator('button[aria-label="Spindle clockwise"]'), "spindle_cw");
await click(main.locator('button[aria-label="Spindle counter-clockwise"]'), "spindle_ccw");
await click(main.getByRole("button", { name: "Stop", exact: true }).nth(1), "spindle_stop");
await click(main.getByRole("button", { name: "Stop spindle", exact: true }), "spindle_stop");
const brake = main.getByLabel("Brake", { exact: true });
if (await brake.count() && !await brake.isDisabled()) {
await brake.click();
@@ -158,7 +157,7 @@ try {
await wait((s) => s.operation === "Operation IDLE" && ["loaded", "aborted"].includes(s.program), "program_aborted");
await click(main.locator(".program-actions button").first(), "program_reload_before_step");
await wait((s) => s.program === "loaded" && s.error === null, "program_reload_before_step_ack");
await click(main.locator(".program-actions button").nth(2), "program_step");
await click(main.locator('button[aria-label="Execute next line [T]"]'), "program_step");
await wait((s) => s.program === "paused", "program_stepped");
await click(main.locator('button[aria-label="Stop program execution [ESC]"]'), "program_step_abort");
await wait((s) => s.operation === "Operation IDLE" && ["loaded", "aborted"].includes(s.program), "program_step_abort_ack");
@@ -169,28 +168,23 @@ try {
await click(main.locator('button[aria-label="Stop program execution [ESC]"]'), "program_abort_again");
await wait((s) => s.operation === "Operation IDLE" && ["loaded", "aborted"].includes(s.program) && s.error === null, "program_abort_again_ack");
}));
results.push(await record("RUN STARTUP SEQUENCE", async () => {
await click(main.locator(".program-actions button").first(), "startup_sequence_reload");
await wait((s) => s.program === "loaded" && s.queue === "0" && s.runEnabled === "true" && !s.programLoading && s.error === null, "startup_sequence_reload_ack");
await click(main.getByRole("button", { name: "Run startup sequence", exact: true }), "startup_sequence");
await wait((s) => s.program === "running", "startup_sequence_running");
await click(main.locator('button[aria-label="Stop program execution [ESC]"]'), "startup_sequence_abort");
await wait((s) => s.operation === "Operation IDLE" && ["loaded", "aborted"].includes(s.program) && s.error === null, "startup_sequence_abort_ack");
}));
results.push(await record("EDITOR PROGRAM LOAD + REOPEN", async () => {
results.push(await record("EDITOR PROGRAM LOAD + RELOAD", async () => {
const editor = main.locator("#axis-program");
await editor.fill("G21\nG1 X2 Y2 Z2 F100\nM2\n");
await click(main.locator(".program-actions button").first(), "editor_program_load");
await wait((s) => s.program === "loaded" && s.command?.includes("PROGRAM_LOAD acknowledged") && s.error === null, "editor_program_load_ack");
await click(main.locator('button[aria-label="Reopen editor defaults"]'), "reopen_defaults");
await wait((s) => s.program === "loaded" && s.case?.includes("xyzac_switchkins") && s.error === null, "reopen_defaults_ack");
await click(main.locator('button[aria-label="Reload current G-code [Ctrl-R]"]'), "reload_current_program");
await wait((s) => s.program === "loaded" && s.command?.includes("PROGRAM_LOAD acknowledged") && s.error === null, "reload_current_program_ack");
}));
results.push(await record("DISABLED unsupported buttons", async () => {
for (const name of ["Toggle skip lines with slash", "Toggle optional pause"]) if (!await main.locator(`button[aria-label="${name}"]`).isDisabled()) throw new Error(`${name}:must_be_disabled`);
}));
} finally {
const buttons = await main.locator("button").evaluateAll((items) => items.map((button) => ({ text: button.textContent?.trim() ?? "", aria: button.getAttribute("aria-label"), disabled: button.disabled })));
const visibleLabels = buttons.map((button) => button.aria ?? button.text).filter(Boolean);
const duplicateLabels = Object.entries(visibleLabels.reduce((counts, label) => { counts[label] = (counts[label] ?? 0) + 1; return counts; }, {})).filter(([, count]) => count > 1);
for (const [label, count] of duplicateLabels) if (["Emergency Stop [F1]", "Power on [F2]", "Home all axes", "Run program", "Clear plot", "Stop Jog", "Stop Spindle"].includes(label)) failures.push({ name: "duplicate_visible_control", result: "failed", reason: `${label}:${count}` });
await page.screenshot({ path: "/tmp/xyzac-panel-buttons-browser.png", fullPage: true });
console.log(JSON.stringify({ schema: "xyzac-panel-buttons-browser.v1", status: failures.length === 0 && browserErrors.length === 0 ? "verified" : "failed", url, results, button_count: buttons.length, disabled_button_count: buttons.filter((button) => button.disabled).length, failures, browser_errors: browserErrors, final_state: await state(), screenshot: "/tmp/xyzac-panel-buttons-browser.png" }));
console.log(JSON.stringify({ schema: "xyzac-panel-buttons-browser.v1", status: failures.length === 0 && browserErrors.length === 0 ? "verified" : "failed", url, results, button_count: buttons.length, disabled_button_count: buttons.filter((button) => button.disabled).length, duplicate_labels: duplicateLabels, failures, browser_errors: browserErrors, final_state: await state(), screenshot: "/tmp/xyzac-panel-buttons-browser.png" }));
await browser.close();
}