Enforce complete AXIS control inventory

This commit is contained in:
2026-09-07 15:48:49 +08:00
parent 0a679b5eeb
commit bc69c513e3
12 changed files with 261 additions and 54 deletions

View File

@@ -13,8 +13,15 @@ const browser = await playwright.chromium.launch({ headless: true, executablePat
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 }, ignoreHTTPSErrors: true });
const failures = [];
const browserErrors = [];
const dialogs = [];
page.on("pageerror", (error) => browserErrors.push(`pageerror:${error.message}`));
page.on("console", (message) => { if (message.type() === "error") browserErrors.push(`console:${message.text()}`); });
page.on("dialog", async (dialog) => {
dialogs.push({ type: dialog.type(), message: dialog.message() });
if (dialog.type() === "prompt" && dialog.message().startsWith("Touch Off:")) await dialog.accept("0");
else if (dialog.type() === "prompt" && dialog.message().startsWith("Coordinate system")) await dialog.accept("P1 G54");
else await dialog.dismiss();
});
const main = page.locator("main.axis-app");
const state = () => main.evaluate((element) => ({
authority: element.dataset.axisAuthority,
@@ -88,21 +95,56 @@ try {
}));
results.push(await record("VIEW buttons", async () => {
for (const name of ["Zoom in", "Zoom out", "Top view", "Rotated top view", "Side view", "Front view", "Inverted front view", "Perspective view", "Drag / rotate mode"]) await click(main.locator(`button[aria-label="${name}"]`), `view_${name}`);
for (const id of ["preview-panLeft", "preview-panRight", "preview-panUp", "preview-panDown", "preview-reset"]) await click(main.locator(`button[data-axis-control="${id}"]`), id);
await main.locator('button[data-axis-control="tab-dro"]').click();
await main.locator('select[data-axis-control="dro-coordinate"]').selectOption("relative");
await main.locator('select[data-axis-control="dro-display"]').selectOption("commanded");
await main.locator('select[data-axis-control="dro-units"]').selectOption("inch");
await main.locator('select[data-axis-control="dro-coordinate"]').selectOption("machine");
await main.locator('select[data-axis-control="dro-display"]').selectOption("actual");
await main.locator('select[data-axis-control="dro-units"]').selectOption("mm");
await main.locator('button[data-axis-control="tab-preview"]').click();
for (const layer of ["path", "live", "limits", "velocity", "offsets", "extents"]) {
const checkbox = main.locator(`input[data-axis-control="preview-layer-${layer}"]`);
await checkbox.click();
await checkbox.click();
}
}));
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 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");
if (!await main.locator('[data-axis-control="manual-override-limits"]').isDisabled()) throw new Error("override_limits_must_be_disabled");
}));
results.push(await record("JOG XYZAC", async () => {
for (const axis of ["X", "Y", "Z", "A", "C"]) {
const axisInput = main.locator(`input[name="axis"][value="${axis.toLowerCase()}"]`);
if (!await axisInput.count()) throw new Error(`axis_${axis}:missing`);
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 jog", exact: true }), `jog_${axis}_stop`);
const jogButton = main.locator(`button[aria-label="Jog ${axis} positive"]`);
await jogButton.dispatchEvent("pointerdown", { pointerId: 1, button: 0 });
await wait((s) => s.error === null && s.command?.includes("JOG_START acknowledged"), `jog_${axis}_start_ack`);
await page.waitForTimeout(100);
await jogButton.dispatchEvent("pointerup", { pointerId: 1, button: 0 });
await wait((s) => s.error === null && s.command?.includes("JOG_STOP acknowledged"), `jog_${axis}_stop_ack`);
}
}));
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"]) {
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`);
await adjust.dispatchEvent("pointerup", { pointerId: 1, button: 0 });
await wait((s) => s.error === null && s.command?.includes("SPINDLE_CONSTANT acknowledged"), `${id}_release_ack`);
}
await click(main.locator('button[aria-label="Spindle counter-clockwise"]'), "spindle_ccw");
await click(main.getByRole("button", { name: "Stop spindle", exact: true }), "spindle_stop");
const brake = main.getByLabel("Brake", { exact: true });
@@ -125,12 +167,6 @@ try {
}
}
}));
results.push(await record("TOOL/PROBE", async () => {
for (const name of ["Prepare T1", "Change", "Arm Probe", "Disarm"]) {
const clicked = await click(main.getByRole("button", { name, exact: true }), `device_${name}`, { required: false });
if (clicked) await wait((s) => s.error === null, `device_${name}_ack`);
}
}));
results.push(await record("OVERRIDES", async () => {
for (const [name, value] of [["Jog speed", "50"], ["Feed override", "50"], ["Spindle override", "50"], ["Max velocity", "50"]]) {
const input = main.locator(`input[aria-label="${name}"]`);
@@ -140,12 +176,12 @@ try {
}
}
}));
results.push(await record("MDI", async () => { await main.getByRole("tab", { name: "MDI [F5]" }).click(); await wait((s) => s.mode === "MDI" || s.command?.includes("SET_MODE acknowledged"), "mdi_mode_ack"); const input = main.locator('.mdi-control input'); await input.fill("G0 X1"); await click(main.getByRole("button", { name: "Go", exact: true }), "mdi_go"); await wait((s) => s.error === null && s.command?.includes("MDI_EXECUTE acknowledged") && s.queue === "0", "mdi_ack"); 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("MDI", async () => { await main.getByRole("tab", { name: "MDI [F5]" }).click(); await wait((s) => s.mode === "MDI" || s.command?.includes("SET_MODE acknowledged"), "mdi_mode_ack"); const input = main.locator('.mdi-control input'); await input.fill("G0 X1"); await click(main.getByRole("button", { name: "Go", exact: true }), "mdi_go"); await wait((s) => s.error === null && s.command?.includes("MDI_EXECUTE acknowledged") && s.queue === "0", "mdi_ack"); const history = main.locator('button[data-axis-control="mdi-history-select"]').first(); await history.click(); if (await input.inputValue() !== "G0 X1") throw new Error("mdi_history_selection_failed"); 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("PROGRAM LOAD/RUN/PAUSE/RESUME/ABORT/RESTART/STEP", async () => {
await main.getByRole("tab", { name: "Manual Control [F3]" }).click();
await wait((s) => s.queue === "0" && s.error === null, "program_preflight_idle");
await page.waitForTimeout(1000);
await click(main.locator(".program-actions button").first(), "program_load");
await click(main.locator('button[data-axis-control="toolbar-reload"]'), "program_load");
await wait((s) => s.program === "loaded" && s.queue === "0" && s.runEnabled === "true" && !s.programLoading && s.error === null, "program_loaded");
await click(main.locator('button[aria-label="Begin executing current file [R]"]'), "program_run");
await wait((s) => s.program === "running", "program_running");
@@ -155,13 +191,13 @@ try {
await wait((s) => s.program === "running", "program_resumed");
await click(main.locator('button[aria-label="Stop program execution [ESC]"]'), "program_abort");
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 click(main.locator('button[data-axis-control="toolbar-reload"]'), "program_reload_before_step");
await wait((s) => s.program === "loaded" && s.error === null, "program_reload_before_step_ack");
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");
await click(main.locator(".program-actions button").first(), "program_reload_before_rerun");
await click(main.locator('button[data-axis-control="toolbar-reload"]'), "program_reload_before_rerun");
await wait((s) => s.program === "loaded" && s.error === null, "program_reload_before_rerun_ack");
await click(main.locator('button[aria-label="Begin executing current file [R]"]'), "program_run_again");
await wait((s) => s.program === "running", "program_running_again");
@@ -171,7 +207,7 @@ try {
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 click(main.locator('button[data-axis-control="toolbar-reload"]'), "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="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");