Files
pleno-vue/tests/unit/use-self-serve-logic-production.spec.js
T

175 lines
6.6 KiB
JavaScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const sessionUserMock = vi.hoisted(() => ({
request: vi.fn(),
objects: {
self_serve_vehicle_conditions: {
get: {
previewAllowed: vi.fn(),
washSummary: vi.fn(),
all: vi.fn(),
},
add: vi.fn(),
delete: vi.fn(),
},
self_serve_tasks: {
attachments: {
list: vi.fn(),
download: vi.fn(),
},
},
},
}));
vi.mock("@/components/session/token/SessionUser.vue", () => ({
SessionUser: sessionUserMock,
}));
import { useSelfServeLogic } from "@/composables/useSelfServeLogic.js";
function deferred() {
let resolve;
let reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
const previewPayload = (overrides = {}) => ({
lane: { id: 7, name: "7" },
session: { id: 501, vehicle_type_id: 2 },
allowed_services: ["manual"],
questions: [{ id: 11, question: "Clean?", answer: null, order_priority: 1 }],
conditions: [],
rules: [],
tasks: [{ id: 91, task: "Manual prep", order_priority: 1, services: ["manual"] }],
...overrides,
});
const summaryPayload = (overrides = {}) => ({
session: { id: 501, vehicle_type_id: 2, allowed: true },
lane: { id: 7, name: "7" },
allowed_services: ["machine", "manual"],
questions: [{ id: 21, question: "Roof?", answer: true, order_priority: 2 }],
conditions: [],
rules: [],
tasks: [{ id: 92, task: "Machine start", order_priority: 2, services: ["machine"] }],
events: [],
...overrides,
});
describe("useSelfServeLogic production behavior", () => {
beforeEach(() => {
vi.clearAllMocks();
sessionUserMock.objects.self_serve_tasks.attachments.list.mockResolvedValue([]);
sessionUserMock.objects.self_serve_tasks.attachments.download.mockResolvedValue({ data: { download_link: "" } });
});
it("merges preview and summary questions while preserving preview answers until summary replaces them", async () => {
sessionUserMock.objects.self_serve_vehicle_conditions.get.previewAllowed.mockResolvedValue(previewPayload());
sessionUserMock.objects.self_serve_vehicle_conditions.get.washSummary.mockResolvedValue(summaryPayload());
const logic = useSelfServeLogic();
await logic.fetchSelfServeData(6, 2, 7, "ab12345");
expect(logic.questions.value.map((question) => question.id)).toEqual([11, 21]);
expect(logic.answers.value).toEqual({ 11: null, 21: true });
expect(logic.visibleQuestions.value.map((question) => question.id)).toEqual([21]);
expect(logic.tasks.value.map((task) => task.id)).toEqual([92]);
expect(logic.allowedServices.value).toEqual(["MACHINE", "MANUAL"]);
});
it("keeps preview questions visible while session summary is still loading", async () => {
const slowSummary = deferred();
sessionUserMock.objects.self_serve_vehicle_conditions.get.previewAllowed.mockResolvedValue(
previewPayload({
questions: [{ id: 11, question: "Clean?", answer: true, order_priority: 1 }],
tasks: [],
})
);
sessionUserMock.objects.self_serve_vehicle_conditions.get.washSummary.mockReturnValue(slowSummary.promise);
const logic = useSelfServeLogic();
const fetchPromise = logic.fetchSelfServeData(6, 2, 7, "ab12345");
await vi.waitFor(() => {
expect(logic.loading.value).toBe(true);
expect(logic.visibleQuestions.value.map((question) => question.id)).toEqual([11]);
});
slowSummary.resolve(summaryPayload());
await fetchPromise;
expect(logic.loading.value).toBe(false);
expect(logic.visibleQuestions.value.map((question) => question.id)).toEqual([21]);
});
it("ignores stale preview and summary responses when a newer request wins the race", async () => {
const slowPreview = deferred();
sessionUserMock.objects.self_serve_vehicle_conditions.get.previewAllowed
.mockReturnValueOnce(slowPreview.promise)
.mockResolvedValueOnce(
previewPayload({ session: { id: 502 }, questions: [{ id: 31, question: "Newest?", answer: false }] })
);
sessionUserMock.objects.self_serve_vehicle_conditions.get.washSummary.mockResolvedValue(
summaryPayload({ session: { id: 502 }, questions: [{ id: 32, question: "Newest summary?", answer: true }] })
);
const logic = useSelfServeLogic();
const stale = logic.fetchSelfServeData(6, 2, 7, "old123");
const fresh = await logic.fetchSelfServeData(6, 2, 7, "new123");
slowPreview.resolve(previewPayload({ session: { id: 501 }, questions: [{ id: 11, question: "Old?" }] }));
await stale;
expect(fresh.session.id).toBe(502);
expect(logic.questions.value.map((question) => question.id)).toEqual([31, 32]);
expect(logic.preview.value.session.id).toBe(502);
expect(logic.loading.value).toBe(false);
});
it("updates allowed services from the lane endpoint and falls back to active task services on first failure", async () => {
const logic = useSelfServeLogic();
sessionUserMock.objects.self_serve_vehicle_conditions.get.previewAllowed.mockResolvedValue(
previewPayload({ allowed_services: [], tasks: [{ id: 91, task: "Machine", services: ["MACHINE", "manual"] }] })
);
sessionUserMock.objects.self_serve_vehicle_conditions.get.washSummary.mockResolvedValue(
summaryPayload({ questions: [] })
);
await logic.fetchSelfServeData(6, 2, 7, "ab12345");
sessionUserMock.request.mockResolvedValueOnce({
data: { success: true, data: { allowed_services: ["relay", "machine"] } },
});
await logic.updateLaneAllowedServices(7);
expect(logic.allowedServices.value).toEqual(["RELAY", "MACHINE"]);
sessionUserMock.request.mockRejectedValueOnce(new Error("edge offline"));
await expect(logic.updateLaneAllowedServices(7)).rejects.toThrow("edge offline");
expect(logic.allowedServices.value).toEqual(["RELAY", "MACHINE"]);
expect(logic.error.value).toBe("edge offline");
});
it("keeps the local answer optimistic only through successful answer sync and reports failures", async () => {
sessionUserMock.objects.self_serve_vehicle_conditions.add.mockRejectedValueOnce(new Error("answer sync failed"));
const logic = useSelfServeLogic();
logic.answerQuestion(11, true);
await expect(
logic.syncVehicleAnswer({
departmentId: 6,
laneId: 7,
reg: "AB12345",
questionId: 11,
value: false,
vehicleTypeId: 2,
})
).rejects.toThrow("answer sync failed");
expect(logic.answers.value[11]).toBe(true);
expect(logic.error.value).toBe("answer sync failed");
expect(logic.loading.value).toBe(false);
});
});