Files
pleno-vue/tests/unit/self-serve-machine-relay-controls.spec.js
T
Jeppe Bundgaard b39b458f4f Add .prettierrc.json and refactor test files for improved formatting consistency:
- Introduced `.prettierrc.json` to enforce consistent code formatting across the project.
- Updated unit and e2e test files to address formatting issues, improve readability, and ensure alignment with the new Prettier configuration.
2026-04-13 09:13:27 +02:00

388 lines
12 KiB
JavaScript

// @vitest-environment jsdom
import { ref } from "vue";
import { mount } from "@vue/test-utils";
import { beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
openGate: vi.fn(() => Promise.resolve()),
openOutsideEntranceGate: vi.fn(() => Promise.resolve()),
openOutsideExitGate: vi.fn(() => Promise.resolve()),
fetchAllRelayStatuses: vi.fn(() => Promise.resolve()),
startPolling: vi.fn(() => Promise.resolve()),
stopPolling: vi.fn(),
toggleRelay: vi.fn(() => Promise.resolve()),
useMachineConnectivity: vi.fn(),
getDepartmentOutsideGateCapabilities: vi.fn(),
}));
const buildConnectivity = () => ({
statuses: ref({
MACHINE: null,
PROGRAM_PICKER: null,
CLEANER: null,
}),
loading: ref({
MACHINE: false,
PROGRAM_PICKER: false,
CLEANER: false,
}),
commandLoading: ref({
command: false,
forceEnable: false,
forceDisable: false,
openEntranceGate: false,
openExitGate: false,
openOutsideEntranceGate: false,
openOutsideExitGate: false,
}),
error: ref(null),
inProgressDetails: ref(null),
inProgressLoading: ref(false),
toggleRelay: mocks.toggleRelay,
openGate: mocks.openGate,
openOutsideEntranceGate: mocks.openOutsideEntranceGate,
openOutsideExitGate: mocks.openOutsideExitGate,
fetchAllRelayStatuses: mocks.fetchAllRelayStatuses,
startPolling: mocks.startPolling,
stopPolling: mocks.stopPolling,
});
vi.mock("@/views/dashboards/superUserDashboard/selfserve/components/SelfServeMachineConnectivity.vue", () => ({
parseRelayStatus: () => "MAINTENANCE",
relayKinds: {
MACHINE: "MACHINE",
PROGRAM_PICKER: "PROGRAM_PICKER",
CLEANER: "CLEANER",
},
useMachineConnectivity: mocks.useMachineConnectivity,
}));
vi.mock("@/composables/departmentOutsideGateCapabilities.js", () => ({
getDepartmentOutsideGateCapabilities: mocks.getDepartmentOutsideGateCapabilities,
}));
import SelfServeMachineRelayControls from "@/views/dashboards/superUserDashboard/selfserve/displays/machine/SelfServeMachineRelayControls.vue";
const flushMicrotasks = async () => {
await Promise.resolve();
await Promise.resolve();
};
describe("SelfServeMachineRelayControls gate controls", () => {
beforeEach(() => {
mocks.openGate.mockReset();
mocks.openOutsideEntranceGate.mockReset();
mocks.openOutsideExitGate.mockReset();
mocks.fetchAllRelayStatuses.mockReset();
mocks.startPolling.mockReset();
mocks.stopPolling.mockReset();
mocks.toggleRelay.mockReset();
mocks.useMachineConnectivity.mockReset();
mocks.getDepartmentOutsideGateCapabilities.mockReset();
mocks.openGate.mockResolvedValue(undefined);
mocks.openOutsideEntranceGate.mockResolvedValue(undefined);
mocks.openOutsideExitGate.mockResolvedValue(undefined);
mocks.fetchAllRelayStatuses.mockResolvedValue(undefined);
mocks.startPolling.mockResolvedValue(undefined);
mocks.useMachineConnectivity.mockImplementation(() => buildConnectivity());
mocks.getDepartmentOutsideGateCapabilities.mockResolvedValue({
hasOutsideEntranceGate: true,
hasOutsideExitGate: true,
});
});
it("shows a single full-width Open Gate button when entrance/exit share the same relay id", async () => {
const wrapper = mount(SelfServeMachineRelayControls, {
props: {
machine: {
id: 1,
name: "Lane 1",
status: "ONLINE",
relay_in_id: "gate-a",
relay_out_id: "gate-a",
},
},
global: {
stubs: {
SelfServeMachineStatus: { template: "<div />" },
BSwitch: { template: "<div />" },
},
},
});
await flushMicrotasks();
expect(wrapper.text()).toContain("Open Gate");
expect(wrapper.text()).not.toContain("Open entrance gate");
expect(wrapper.text()).not.toContain("Open exit gate");
const button = wrapper.get("button.is-primary");
await button.trigger("click");
expect(mocks.openGate).toHaveBeenCalledWith("ENTRANCE");
expect(mocks.fetchAllRelayStatuses).toHaveBeenCalledTimes(1);
expect(mocks.startPolling).toHaveBeenCalledTimes(1);
});
it("shows separate entrance/exit controls when gate relay ids differ", async () => {
const wrapper = mount(SelfServeMachineRelayControls, {
props: {
machine: {
id: 2,
name: "Lane 2",
status: "ONLINE",
relay_in_id: "gate-entrance",
relay_out_id: "gate-exit",
},
},
global: {
stubs: {
SelfServeMachineStatus: { template: "<div />" },
BSwitch: { template: "<div />" },
},
},
});
await flushMicrotasks();
expect(wrapper.text()).not.toContain("Open Gate");
expect(wrapper.text()).toContain("Open entrance gate");
expect(wrapper.text()).toContain("Open exit gate");
await wrapper.get("button.is-info").trigger("click");
await wrapper.get("button.is-link").trigger("click");
expect(mocks.openGate).toHaveBeenNthCalledWith(1, "ENTRANCE");
expect(mocks.openGate).toHaveBeenNthCalledWith(2, "EXIT");
});
it("shows no in-progress wash details when lane is idle", async () => {
const wrapper = mount(SelfServeMachineRelayControls, {
props: {
machine: {
id: 3,
name: "Lane 3",
status: "ONLINE",
relay_in_id: "in",
relay_out_id: "out",
},
},
global: {
stubs: {
SelfServeMachineStatus: { template: "<div />" },
BSwitch: { template: "<div />" },
},
},
});
await flushMicrotasks();
expect(wrapper.text()).toContain("No wash in progress.");
});
it("renders customer and vehicle details when wash is in progress", async () => {
mocks.useMachineConnectivity.mockImplementation(() => ({
...buildConnectivity(),
inProgressDetails: ref({
lane_id: 4,
in_progress: true,
session: { reg: "AB12345", customer_number: 1001 },
customer: { display_name: "John Doe", customer_number: 1001 },
vehicle: { reg: "AB12345" },
}),
}));
const wrapper = mount(SelfServeMachineRelayControls, {
props: {
machine: {
id: 4,
name: "Lane 4",
status: "ONLINE",
relay_in_id: "in",
relay_out_id: "out",
},
},
global: {
stubs: {
SelfServeMachineStatus: { template: "<div />" },
BSwitch: { template: "<div />" },
},
},
});
await flushMicrotasks();
expect(wrapper.text()).toContain("Customer");
expect(wrapper.text()).toContain("John Doe");
expect(wrapper.text()).toContain("Vehicle");
expect(wrapper.text()).toContain("AB12345");
});
it("keeps existing in-progress content visible while refresh is loading", async () => {
mocks.useMachineConnectivity.mockImplementation(() => ({
...buildConnectivity(),
inProgressDetails: ref({
lane_id: 5,
in_progress: true,
session: { reg: "CD67890", customer_number: 2002 },
customer: { display_name: "Jane Doe", customer_number: 2002 },
vehicle: { reg: "CD67890" },
}),
inProgressLoading: ref(true),
}));
const wrapper = mount(SelfServeMachineRelayControls, {
props: {
machine: {
id: 5,
name: "Lane 5",
status: "ONLINE",
relay_in_id: "in",
relay_out_id: "out",
},
},
global: {
stubs: {
SelfServeMachineStatus: { template: "<div />" },
BSwitch: { template: "<div />" },
},
},
});
await flushMicrotasks();
expect(wrapper.text()).toContain("Customer");
expect(wrapper.text()).toContain("Jane Doe");
expect(wrapper.text()).not.toContain("Loading in-progress details...");
});
it("shows and wires both outside gate buttons when department has outside entrance and exit gates", async () => {
const wrapper = mount(SelfServeMachineRelayControls, {
props: {
machine: {
id: 6,
department: 12,
name: "Lane 6",
status: "ONLINE",
relay_in_id: "in",
relay_out_id: "out",
},
},
global: {
stubs: {
SelfServeMachineStatus: { template: "<div />" },
BSwitch: { template: "<div />" },
},
},
});
await flushMicrotasks();
const outsideEntranceButton = wrapper.get("[data-testid='outside-entrance-gate-button']");
const outsideExitButton = wrapper.get("[data-testid='outside-exit-gate-button']");
expect(outsideEntranceButton.exists()).toBe(true);
expect(outsideExitButton.exists()).toBe(true);
await outsideEntranceButton.trigger("click");
await outsideExitButton.trigger("click");
expect(mocks.openOutsideEntranceGate).toHaveBeenCalledTimes(1);
expect(mocks.openOutsideExitGate).toHaveBeenCalledTimes(1);
});
it("shows only the outside entrance button when department only has an outside entrance gate", async () => {
mocks.getDepartmentOutsideGateCapabilities.mockResolvedValueOnce({
hasOutsideEntranceGate: true,
hasOutsideExitGate: false,
});
const wrapper = mount(SelfServeMachineRelayControls, {
props: {
machine: {
id: 7,
department: 13,
name: "Lane 7",
status: "ONLINE",
relay_in_id: "in",
relay_out_id: "out",
},
},
global: {
stubs: {
SelfServeMachineStatus: { template: "<div />" },
BSwitch: { template: "<div />" },
},
},
});
await flushMicrotasks();
expect(wrapper.find("[data-testid='outside-entrance-gate-button']").exists()).toBe(true);
expect(wrapper.find("[data-testid='outside-exit-gate-button']").exists()).toBe(false);
});
it("shows only the outside exit button when department only has an outside exit gate", async () => {
mocks.getDepartmentOutsideGateCapabilities.mockResolvedValueOnce({
hasOutsideEntranceGate: false,
hasOutsideExitGate: true,
});
const wrapper = mount(SelfServeMachineRelayControls, {
props: {
machine: {
id: 8,
department: 14,
name: "Lane 8",
status: "ONLINE",
relay_in_id: "in",
relay_out_id: "out",
},
},
global: {
stubs: {
SelfServeMachineStatus: { template: "<div />" },
BSwitch: { template: "<div />" },
},
},
});
await flushMicrotasks();
expect(wrapper.find("[data-testid='outside-entrance-gate-button']").exists()).toBe(false);
expect(wrapper.find("[data-testid='outside-exit-gate-button']").exists()).toBe(true);
});
it("hides outside gate controls when department has no outside gates", async () => {
mocks.getDepartmentOutsideGateCapabilities.mockResolvedValueOnce({
hasOutsideEntranceGate: false,
hasOutsideExitGate: false,
});
const wrapper = mount(SelfServeMachineRelayControls, {
props: {
machine: {
id: 9,
department: 15,
name: "Lane 9",
status: "ONLINE",
relay_in_id: "in",
relay_out_id: "out",
},
},
global: {
stubs: {
SelfServeMachineStatus: { template: "<div />" },
BSwitch: { template: "<div />" },
},
},
});
await flushMicrotasks();
expect(wrapper.find("[data-testid='outside-entrance-gate-button']").exists()).toBe(false);
expect(wrapper.find("[data-testid='outside-exit-gate-button']").exists()).toBe(false);
expect(wrapper.text()).not.toContain("Open outside entrance gate");
expect(wrapper.text()).not.toContain("Open outside exit gate");
});
});