Files
pleno-vue/tests/unit/self-serve-dynamic-image.spec.js
T
Jeppe B 19fbbcddce test(pleno-vue): snapshot-lock SELF_SERVE_TASK_BUTTON_OPTIONS shape (TRU-19) (#316)
Adds a snapshot test that locks the full {id, name, description} triple for every entry in SELF_SERVE_TASK_BUTTON_OPTIONS. The previous test only checked counts and that the ids were sequential; this test catches reordering, renames, and unexpected entries as well.

The wash bay exposes 12 programs and 3 special buttons (reset / program picker / start). The dashboard renders one entry per program number from this array, so the entire array must remain exactly 15 entries long.

Test result: 10/10 vitest passed (was 9, +1 new snapshot test)

Refs TRU-19
2026-08-16 13:00:19 +02:00

172 lines
7.8 KiB
JavaScript

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", () => {
const presentation = getSelfServeTaskDynamicImagePresentation({
buttons: ["program_picker", 0],
});
expect(presentation.buttons).toEqual(["program_picker"]);
expect(presentation.normalizedButtons).toEqual(["program_picker", 0]);
expect(presentation.thumbPosition).toBe(1);
});
it("prefers configured program wheel thumb position and keeps numeric machine buttons highlighted", () => {
const presentation = getSelfServeTaskDynamicImagePresentation({
services: ["PROGRAM_PICKER"],
buttons: [2, "start"],
dynamic_images_vehicle_type: 8,
});
expect(presentation.buttons).toEqual(["program_picker", 2, "start"]);
expect(presentation.normalizedButtons).toEqual([2, "start"]);
expect(presentation.thumbPosition).toBe(8);
});
it("drops the legacy numeric wheel selector while keeping other machine buttons highlighted", () => {
const presentation = getSelfServeTaskDynamicImagePresentation({
services: ["PROGRAM_PICKER"],
buttons: [3, 1, "start"],
dynamic_images_vehicle_type: 4,
});
expect(presentation.buttons).toEqual(["program_picker", 1, "start"]);
expect(presentation.normalizedButtons).toEqual([3, 1, "start"]);
expect(presentation.thumbPosition).toBe(4);
});
it("keeps numeric buttons highlighted outside program-picker mode", () => {
const presentation = getSelfServeTaskDynamicImagePresentation({
services: ["MACHINE"],
buttons: [2, "start"],
dynamic_images_vehicle_type: 8,
});
expect(presentation.buttons).toEqual([2, "start"]);
expect(presentation.thumbPosition).toBe(8);
});
it("excludes program-picker thumb selectors from completed step count", () => {
const tasks = [
{
id: 10,
services: ["PROGRAM_PICKER"],
buttons: [2, "start"],
},
];
expect(getSelfServeCompletedDynamicImageStep(tasks, { 10: true })).toBe(2);
});
it("keeps only one numbered program button in program-picker mode", () => {
expect(normalizeSelfServeProgramPickerTaskButtons(["program_picker", 1, 2, "start"], [], 2)).toEqual([
"program_picker",
2,
"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);
});
// Snapshot lock for the full button-registry shape (TRU-19).
//
// The wash bay exposes 12 programs and 3 special buttons (reset / program
// picker / start). The dashboard renders one entry per program number
// from SELF_SERVE_TASK_BUTTON_OPTIONS, so the entire array must remain
// exactly 15 entries long: 1 reset + 1 program picker + 12 numeric
// programs (ids 0-11) + 1 start.
//
// If anyone adds, removes, or reorders entries without thinking about
// the operator UI and the api mapping, this snapshot will fail and
// force a deliberate update. The user-facing program names ("FF Uvs",
// "10min", "SF", etc.) live on the wash bay hardware and are NOT in
// this array — see TRU-19 and api SelfserveProgramRegistryContractTest.
it("locks the full SELF_SERVE_TASK_BUTTON_OPTIONS shape as a snapshot (15 entries, fixed order)", () => {
// Exact total length: 1 reset + 1 program picker + 12 programs + 1 start = 15
expect(SELF_SERVE_TASK_BUTTON_OPTIONS).toHaveLength(15);
// Full structural snapshot. The {id, name, description} triples must
// remain exactly as below. If you change one, change them all here
// deliberately.
expect(SELF_SERVE_TASK_BUTTON_OPTIONS.map((entry) => [entry.id, entry.name, entry.description])).toEqual([
[SELF_SERVE_TASK_BUTTON_RESET, "Reset", "Reset button"],
[SELF_SERVE_TASK_BUTTON_PROGRAM_PICKER, "Program picker", "Program picker wheel"],
[0, "Program 1", "Machine button 0"],
[1, "Program 2", "Machine button 1"],
[2, "Program 3", "Machine button 2"],
[3, "Program 4", "Machine button 3"],
[4, "Program 5", "Machine button 4"],
[5, "Program 6", "Machine button 5"],
[6, "Program 7", "Machine button 6"],
[7, "Program 8", "Machine button 7"],
[8, "Program 9", "Machine button 8"],
[9, "Program 10", "Machine button 9"],
[10, "Program 11", "Machine button 10"],
[11, "Program 12", "Machine button 11"],
[SELF_SERVE_TASK_BUTTON_START, "Start", "Start button"],
]);
});
});