Handle "STOP" command failures by interpreting "lane not occupied" as a safe-to-clear state. Refactor error handling, add clearActiveWashState, and extend unit/E2E tests with this scenario.

This commit is contained in:
Jeppe Bundgaard
2026-04-30 14:52:40 +02:00
parent 5d1199a133
commit bdffdca420
3 changed files with 163 additions and 25 deletions
+49
View File
@@ -633,6 +633,55 @@ test.describe("Self-serve wash", () => {
await expect(page.getByTestId("self-serve-nav-complete")).toBeVisible();
});
test("already-ended stop response completes stale in-progress progress", async ({ page }) => {
await seedSavedProgress(page, {
washInProgress: true,
washLaneId: 7,
washStartTime: Date.now() - 30_000,
currentStep: 4,
licensePlateInput: "AB12345",
vehicleTypeSelect: 2,
radioLaneOption: 7,
radioWashType: "Manual",
customerNumberInput: "12345679",
});
await mockApi(page, {
authenticated: true,
permissions: ["user"],
selfServe: {
commandResponses: [
{
success: false,
data: {
message: "Failed to execute command: Cannot stop lane: Lane is not occupied.",
},
},
],
},
});
await primeSession(page, {
token: "self-serve-stop-already-ended-token",
permissions: ["user"],
});
await page.goto("/user/wash/start");
await expect(page.getByTestId("self-serve-live-elapsed")).toBeVisible({ timeout: 10_000 });
await advanceGuidedWashToLastStep(page);
const stopCommandRequestPromise = waitForLaneCommandRequest(page, "STOP");
await page.getByTestId("self-serve-nav-complete").click();
const stopCommandRequest = await stopCommandRequestPromise;
expect(stopCommandRequest.postDataJSON?.()).toMatchObject({
lane_id: 7,
command: "STOP",
});
await expect(page.getByTestId("self-serve-action-error")).toBeHidden();
await expect(page.getByTestId("self-serve-completed-step")).toBeVisible();
await expect(page.getByTestId("self-serve-live-elapsed")).toBeHidden();
});
test("edge gateway preview and summary refreshes stay read-only until confirmation", async ({ page }) => {
const requests = captureSelfServeGatewayRequests(page);
@@ -279,6 +279,51 @@ describe("useWashSessionActions property gate commands", () => {
expect(clearProgress).not.toHaveBeenCalled();
});
it("clears active wash state when stop reports the lane is already unoccupied", async () => {
const washInProgress = ref(true);
const washLaneId = ref(7);
const washStartTime = ref(1_000);
const completedDurationMs = ref(null);
const now = ref(46_000);
const clearProgress = vi.fn();
const stopElapsedTimer = vi.fn();
const request = vi.fn(async (_url, _method, body) => {
if (body.command === "STOP") {
return {
data: {
success: false,
data: {
message: "Failed to execute command: Cannot stop lane: Lane is not occupied.",
},
},
};
}
return { data: { success: true } };
});
const { actions, alertFn } = createActions({
request,
washInProgress,
washLaneId,
washStartTime,
completedDurationMs,
now,
clearProgress,
stopElapsedTimer,
});
const result = await actions.onStopWash(7);
expect(result).toBe(true);
expect(alertFn).not.toHaveBeenCalled();
expect(completedDurationMs.value).toBe(45_000);
expect(washInProgress.value).toBe(false);
expect(washLaneId.value).toBeNull();
expect(stopElapsedTimer).toHaveBeenCalledTimes(1);
expect(clearProgress).toHaveBeenCalledTimes(1);
});
it("clears active wash state only after the stop command succeeds", async () => {
const washInProgress = ref(true);
const washLaneId = ref(7);