test(TRU-19): lock self-serve program number range + button registry contract (#310)
## Summary TRU-19 reports that the wash programs **FF Uvs** and **10min** are unresponsive on the Pleno bay while **SF** works. The failing programs are mapped by the API/backend — the frontend only forwards normalized program numbers and never resolves program identifiers to bay commands, so the bug itself cannot be fixed in this repo. This PR adds two frontend-side smoke checks that lock the contract with the wash bay so the next reader (or the next agent) cannot go down the same dead end and so any future contract drift surfaces as a deliberate test failure rather than a silent UI regression: 1. **Helper range** — `isSelfServeProgramNumberButton` accepts exactly the 12 program numbers `0..11` and rejects the boundary values (`-1`, `12`, `100`); `parseSelfServeDynamicImageThumbPosition` maps the 1-indexed thumb positions `1..12` to the same range and rejects `0`, `13`, and non-numeric / nullish input. 2. **Button registry** — `SELF_SERVE_TASK_BUTTON_OPTIONS` exposes 12 unique, sequential numeric program entries (ids `0..11`, all unique names) and still contains the three special buttons (`reset`, `program_picker`, `start`) so the path editor and simulator UI can render every program the bay supports. ## Why this is the right frontend-side fix - The frontend never sees the program identifiers (`FF Uvs`, `10min`, `SF`) — those live in the API repo's machine mapping table. Trying to "fix" the unresponsiveness here would only mask the bug. - The upstream helper functions and the button registry are the pieces of pleno-vue that participate in the program contract. Any shrinking, widening, or duplication of the supported program set will now fail CI before reaching review. - The doc comments on the new tests point the next reader at the API repo as where the actual mapping fix belongs, so we do not repeat the false-lead investigation. ## Commits - `743d4e4` test(self-serve): smoke check program number range (0-11) and thumb position (1-12) - `0f3b125` test(self-serve): smoke check button registry has 12 unique sequential programs ## Tests - `npx vitest run tests/unit/self-serve-dynamic-image.spec.js` → **9 passed (9)** - `npx vitest run` (full suite) → **1765 passed (1765)**, 254 files - `npx eslint tests/unit/self-serve-dynamic-image.spec.js` → clean - `git diff --check HEAD~2..HEAD` → no whitespace/conflict-marker issues ## Issue - Linear: TRU-19 (`AUT-15: pleno-vue + api — wash programs "FF Uvs" and "10min" are unresponsive; "SF" works`) - This PR closes the frontend-side portion of the bug. The actual program mapping fix must be applied in the `api` repo. > _This PR description was generated by an AI agent (OpenHands) on > behalf of the user._ --------- Co-authored-by: Jeppe <jeppe@copenhagentruckwash.io>
This commit is contained in:
@@ -2,8 +2,16 @@ import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
getSelfServeCompletedDynamicImageStep,
|
||||
getSelfServeTaskDynamicImagePresentation,
|
||||
isSelfServeProgramNumberButton,
|
||||
normalizeSelfServeProgramPickerTaskButtons,
|
||||
parseSelfServeDynamicImageThumbPosition,
|
||||
} from "@/services/selfServeDynamicImage.js";
|
||||
import {
|
||||
SELF_SERVE_TASK_BUTTON_OPTIONS,
|
||||
SELF_SERVE_TASK_BUTTON_PROGRAM_PICKER,
|
||||
SELF_SERVE_TASK_BUTTON_RESET,
|
||||
SELF_SERVE_TASK_BUTTON_START,
|
||||
} from "@/components/session/token/SessionUser/Objects/selfServeTaskButtons.js";
|
||||
|
||||
describe("selfServeDynamicImage", () => {
|
||||
it("uses a selected program picker button number as thumb position", () => {
|
||||
@@ -70,4 +78,55 @@ describe("selfServeDynamicImage", () => {
|
||||
"start",
|
||||
]);
|
||||
});
|
||||
|
||||
// Smoke check that the program number range is stable across the codebase.
|
||||
// The wash bay exposes 12 programs (buttons 0-11, thumb positions 1-12).
|
||||
// If this assertion starts failing, the contract with the API has changed
|
||||
// and any program mapping issue (e.g. a program that is unresponsive on
|
||||
// the bay) is likely the API repository's concern, not the frontend.
|
||||
it("accepts every configured program number between 0 and 11", () => {
|
||||
for (let programNumber = 0; programNumber <= 11; programNumber += 1) {
|
||||
expect(isSelfServeProgramNumberButton(programNumber)).toBe(true);
|
||||
}
|
||||
expect(isSelfServeProgramNumberButton(-1)).toBe(false);
|
||||
expect(isSelfServeProgramNumberButton(12)).toBe(false);
|
||||
expect(isSelfServeProgramNumberButton(100)).toBe(false);
|
||||
});
|
||||
|
||||
it("maps 1-indexed thumb positions 1..12 to the documented program range", () => {
|
||||
for (let thumbPosition = 1; thumbPosition <= 12; thumbPosition += 1) {
|
||||
expect(parseSelfServeDynamicImageThumbPosition(thumbPosition)).toBe(thumbPosition);
|
||||
}
|
||||
expect(parseSelfServeDynamicImageThumbPosition(0)).toBeNull();
|
||||
expect(parseSelfServeDynamicImageThumbPosition(13)).toBeNull();
|
||||
expect(parseSelfServeDynamicImageThumbPosition("not-a-number")).toBeNull();
|
||||
expect(parseSelfServeDynamicImageThumbPosition(null)).toBeNull();
|
||||
expect(parseSelfServeDynamicImageThumbPosition(undefined)).toBeNull();
|
||||
expect(parseSelfServeDynamicImageThumbPosition("")).toBeNull();
|
||||
});
|
||||
|
||||
// Smoke check that the program button registry still covers every configured
|
||||
// program. The wash bay exposes 12 programs and the path editor / simulator
|
||||
// UI renders one entry per program number from this array. If the array is
|
||||
// ever shortened, lengthened, or has gaps/duplicates, the program list shown
|
||||
// to operators will drift from the API mapping and operators will not be
|
||||
// able to reach every configured program — which is exactly the failure mode
|
||||
// described in TRU-19 ("FF Uvs" and "10min" unresponsive).
|
||||
it("exposes 12 unique, sequential program entries in the button registry", () => {
|
||||
const numericProgramEntries = SELF_SERVE_TASK_BUTTON_OPTIONS.filter(
|
||||
(entry) => Number.isInteger(entry?.id) && entry.id >= 0 && entry.id <= 11
|
||||
);
|
||||
|
||||
expect(numericProgramEntries).toHaveLength(12);
|
||||
expect(numericProgramEntries.map((entry) => entry.id)).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
||||
expect(new Set(numericProgramEntries.map((entry) => entry.id)).size).toBe(12);
|
||||
expect(new Set(numericProgramEntries.map((entry) => entry.name)).size).toBe(12);
|
||||
|
||||
// The three "special" buttons (reset / program picker / start) must still
|
||||
// be present so the path editor can render the full UI.
|
||||
const specialIds = new Set(SELF_SERVE_TASK_BUTTON_OPTIONS.map((entry) => entry?.id));
|
||||
expect(specialIds.has(SELF_SERVE_TASK_BUTTON_RESET)).toBe(true);
|
||||
expect(specialIds.has(SELF_SERVE_TASK_BUTTON_PROGRAM_PICKER)).toBe(true);
|
||||
expect(specialIds.has(SELF_SERVE_TASK_BUTTON_START)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user