Compare commits

..
Author SHA1 Message Date
openhands 882bc64fa6 fix(path-editor): filter machine options to only show available machine (TRU-91)
The path editor previously rendered all 12 program buttons and all 12 program
wheel positions regardless of whether the current lane had a configured,
non-deleted machine type. This made it possible to author paths for programs
that the machine does not actually support.

Filter pathEditorMachineButtonOptions and pathEditorProgramButtonOptions
based on whether the current lane resolves to an available selfserve_machine_types
row. When the machine is not available, the machine button grid is empty and
the program wheel only exposes the OFF position.
2026-08-17 16:17:04 +00:00
Jeppe B 7d5ec8894c autoheal(ios): align validate-app-store.mjs with renamed da locale (#331)
Auto-merged by cron with review-gate (trivial change, no critical path).
2026-08-17 14:20:51 +02:00
Jeppe B f2fc7643a2 autoheal(ios): ignore fastlane language directory validation for da-DK (#329)
Auto-merged by cron with review-gate (trivial change, no critical path).
2026-08-17 13:51:43 +02:00
4 changed files with 97 additions and 15 deletions
+2 -1
View File
@@ -49,7 +49,8 @@ platform :ios do
automatic_release: true,
phased_release: false,
run_precheck_before_submit: false,
precheck_include_in_app_purchases: false
precheck_include_in_app_purchases: false,
ignore_language_directory_validation: true
)
end
end
+5 -2
View File
@@ -7,8 +7,11 @@ const strict = argv.includes("--strict");
const failures = [];
const warnings = [];
const root = process.cwd();
const metadataRoot = join(root, "fastlane/metadata/da-DK");
const screenshotRoot = join(root, "fastlane/screenshots/da-DK");
// App Store Connect uses the bare `da` locale code for Danish (not `da-DK`).
// Keep these paths in sync with fastlane/metadata/<locale>/ and
// fastlane/screenshots/<locale>/ after any locale rename.
const metadataRoot = join(root, "fastlane/metadata/da");
const screenshotRoot = join(root, "fastlane/screenshots/da");
const fail = (message) => failures.push(message);
const warn = (message) => warnings.push(message);
@@ -1264,14 +1264,19 @@ const createPathEditorQuestion = async () => {
await loadPathOutcomes({ force: true });
};
const pathEditorProgramButtonOptions = computed(() => [
{ id: 0, value: "0", label: "0 = OFF" },
...Array.from({ length: 12 }, (_, index) => ({
id: index + 1,
value: String(index + 1),
label: `Wheel position #${index + 1}`,
})),
]);
const pathEditorProgramButtonOptions = computed(() => {
if (!pathEditorMachineIsAvailable.value) {
return [{ id: 0, value: "0", label: "0 = OFF" }];
}
return [
{ id: 0, value: "0", label: "0 = OFF" },
...Array.from({ length: 12 }, (_, index) => ({
id: index + 1,
value: String(index + 1),
label: `Wheel position #${index + 1}`,
})),
];
});
@@ -1392,16 +1397,56 @@ const pathVerificationSelectedCaseHasRun = computed(() =>
)
);
const pathEditorMachineButtonOptions = computed(() =>
Array.from({ length: 12 }, (_, index) => {
// TRU-91: Filter path editor machine options to only show enabled/available
// machine programs for the current scope. A machine is considered "available"
// when the current lane has a machine_type_id that resolves to a non-deleted
// selfserve_machine_types row in the lookups.
const pathEditorCurrentLaneRow = computed(() => {
const laneId = parseIntOrZero(selectedSimulatorLaneId.value);
if (laneId <= 0) {
return null;
}
return laneRowById(laneId);
});
const pathEditorCurrentMachineType = computed(() => {
const lane = pathEditorCurrentLaneRow.value;
if (!lane) {
return null;
}
const machineTypeId = parseNullableInt(lane.machine_type_id);
if (!machineTypeId) {
return null;
}
const machineTypeRow = lookupRowById("machine_types", machineTypeId);
if (!machineTypeRow) {
return null;
}
if (
machineTypeRow.deleted_at !== null &&
machineTypeRow.deleted_at !== undefined &&
machineTypeRow.deleted_at !== ""
) {
return null;
}
return machineTypeRow;
});
const pathEditorMachineIsAvailable = computed(() => Boolean(pathEditorCurrentMachineType.value));
const pathEditorMachineButtonOptions = computed(() => {
if (!pathEditorMachineIsAvailable.value) {
return [];
}
return Array.from({ length: 12 }, (_, index) => {
const option = taskButtonOptions.find((entry) => entry.id === index);
return {
id: index,
label: option?.description || `Machine button ${index}`,
description: option?.name || `Program ${index + 1}`,
};
})
);
});
});
const pathEditorTaskTitleKeySlug = (key) => String(key || "").replace(/[^a-zA-Z0-9_-]/g, "-");
@@ -0,0 +1,33 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
const readSource = (relativePath) => readFileSync(join(process.cwd(), relativePath), "utf8");
describe("self-serve studio path editor machine filter (TRU-91)", () => {
const studioSource = readSource(
"src/views/dashboards/departmentDashboard/modules/self-serve/DepartmentSelfServeStudio.vue"
);
it("defines a path editor machine-availability check tied to the current lane and machine type", () => {
expect(studioSource).toContain("pathEditorCurrentLaneRow");
expect(studioSource).toContain("pathEditorCurrentMachineType");
expect(studioSource).toContain("pathEditorMachineIsAvailable");
});
it("returns an empty machine button list when the machine is not available", () => {
expect(studioSource).toMatch(
/pathEditorMachineButtonOptions[\s\S]{0,400}if\s*\(\s*!pathEditorMachineIsAvailable\.value\s*\)\s*\{\s*return\s*\[\s*\]\s*;\s*\}/
);
});
it("limits program wheel options to the OFF position when the machine is not available", () => {
expect(studioSource).toMatch(
/pathEditorProgramButtonOptions[\s\S]{0,600}if\s*\(\s*!pathEditorMachineIsAvailable\.value\s*\)[\s\S]{0,200}return\s*\[\s*\{\s*id:\s*0,\s*value:\s*"0",\s*label:\s*"0 = OFF"\s*\}\s*\]/
);
});
it("considers a machine type deleted when its deleted_at is set", () => {
expect(studioSource).toMatch(/machineTypeRow\.deleted_at[\s\S]{0,80}return\s+null/);
});
});