Align AXIS control flow and fix pause and program rendering

Document source UI gates, explicit native live-plot clearing, and remaining parity boundaries. Fix pause/resume transport continuity, repeated stepping, coordinate previews, and spindle override state. Optimize large program snapshots and rendering, and cover the control flows with native Tcl, WASM, and browser checks.

Validation: 22 native tests and source/browser checks pass. Worker/App regression: 113 of 123 pass; 10 pre-existing failures remain documented with no new failures.
This commit is contained in:
2026-09-08 10:46:27 +08:00
parent 845075f4a3
commit bade560372
55 changed files with 2495 additions and 286 deletions

View File

@@ -113,10 +113,8 @@ try {
results.push(await record("VISUAL CLEAR", async () => { await click(main.locator('button[aria-label="Clear live plot [Ctrl-K]"]'), "visual_clear"); await wait((s) => s.error === null, "visual_clear_ack"); }));
results.push(await record("MANUAL MODE", async () => { await main.getByRole("tab", { name: "Manual Control [F3]" }).click(); await wait((s) => s.mode === "MANUAL" || s.command?.includes("SET_MODE acknowledged"), "manual_mode_ack"); }));
results.push(await record("HOME/TOUCH OFF boundaries", async () => {
const dialogCount = dialogs.length;
await click(main.locator('button[data-axis-control="manual-home-axis"]'), "home_axis_repeat");
await page.waitForTimeout(100);
if (dialogs.length !== dialogCount + 1 || !dialogs.at(-1)?.message.includes("already homed")) throw new Error("home_axis_confirmation_missing");
await wait((s) => s.command?.includes("home_joint_requires_joint_mode"), "world_axis_home_source_rejection");
await click(main.locator('button[data-axis-control="manual-touch-off"]'), "touch_off");
await wait((s) => s.error === null && s.command?.includes("TOUCH_OFF acknowledged"), "touch_off_ack");
if (!await main.locator('[data-axis-control="manual-tool-touch-off"]').isDisabled()) throw new Error("tool_touch_off_must_be_disabled");
@@ -138,7 +136,7 @@ try {
results.push(await record("SPINDLE", async () => {
await click(main.locator('button[aria-label="Spindle clockwise"]'), "spindle_cw");
await wait((s) => s.error === null && s.command?.includes("SPINDLE_COMMAND acknowledged"), "spindle_cw_ack");
for (const id of ["manual-spindle-decrease", "manual-spindle-increase"]) {
for (const id of ["manual-spindle-increase", "manual-spindle-decrease"]) {
const adjust = main.locator(`button[data-axis-control="${id}"]`);
await adjust.dispatchEvent("pointerdown", { pointerId: 1, button: 0 });
await wait((s) => s.error === null && s.command?.includes("SPINDLE_COMMAND acknowledged"), `${id}_press_ack`);
@@ -215,6 +213,30 @@ try {
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`);
}));
results.push(await record("MENUS, ABOUT and CASE SELECTION", async () => {
await main.locator('[data-axis-control="workspace-case-select"]').selectOption({ index: 0 });
await main.locator('[data-axis-control="menu-file"]').click();
for (const id of ["menu-file-edit-tool-data", "menu-file-quit"]) {
const button = main.locator(`[data-axis-control="${id}"]`);
if (!await button.isDisabled() || !await button.getAttribute("title")) throw new Error(`${id}:missing_boundary`);
}
await main.locator('[data-axis-control="menu-help"]').click();
await main.locator('[data-axis-control="menu-help-about"]').click();
await main.locator('[data-axis-control="about-close"]').click();
await main.locator('[data-axis-control="tab-diagnostics"]').click();
await main.locator('[data-axis-control="tab-manual"]').click();
}));
results.push(await record("POWER OFF and ESTOP recovery", async () => {
await click(main.locator('[data-axis-control="toolbar-power"]'), "power_off");
await wait((s) => s.machine !== "Machine ON", "power_off_ack");
await click(main.locator('[data-axis-control="toolbar-estop"]'), "estop");
await wait((s) => s.machine === "Machine ESTOP", "estop_ack");
if (!await main.locator('[data-axis-control="toolbar-power"]').isDisabled()) throw new Error("power_enabled_during_estop");
await click(main.locator('[data-axis-control="toolbar-estop"]'), "release_estop");
await wait((s) => s.machine !== "Machine ESTOP", "release_estop_ack");
await click(main.locator('[data-axis-control="toolbar-power"]'), "power_restore");
await wait((s) => s.machine === "Machine ON", "power_restore_ack");
}));
} 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);
@@ -222,5 +244,6 @@ try {
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, duplicate_labels: duplicateLabels, failures, browser_errors: browserErrors, final_state: await state(), screenshot: "/tmp/xyzac-panel-buttons-browser.png" }));
if (failures.length || browserErrors.length) process.exitCode = 1;
await browser.close();
}