175 lines
6.6 KiB
JavaScript
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);
|
|
});
|
|
});
|