Add unit tests and logic for department self-serve enablement:

- Implemented department self-serve status management in `SelfServeMachine` with fetching, updating, and error handling.
- Added unit tests for status retrieval, toggle behavior, and error scenarios.
- Enhanced UI with `b-switch` for self-serve enablement control.
This commit is contained in:
Jeppe Bundgaard
2026-03-25 14:50:07 +01:00
parent caec066c6c
commit 844d57b1a5
2 changed files with 235 additions and 5 deletions
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { defineProps } from "vue";
import { computed, onMounted, ref, watch } from "vue";
import SelfServeMachineStatus
from "@/views/dashboards/superUserDashboard/selfserve/displays/machine/SelfServeMachineStatus.vue";
import type {Machine} from "@/views/dashboards/superUserDashboard/selfserve/types/MachineType.vue";
@@ -8,11 +8,79 @@ import SelfServeMachineControls
import WhiteBoxCard from "@/components/displays/boxes/WhiteBoxCard.vue";
import SelfServeMachineRelayControls
from "@/views/dashboards/superUserDashboard/selfserve/displays/machine/SelfServeMachineRelayControls.vue";
import { departments, getDepartmentName} from "@/components/pagination/departmentTabs.vue";
import { getDepartmentName } from "@/components/pagination/departmentTabs.vue";
import {
getDepartmentSelfServeEnabled,
setDepartmentSelfServeEnabled,
} from "@/composables/departmentSelfServeEnabled.js";
import {BSwitch} from "buefy";
// Props = machine type
defineProps<{
const props = defineProps<{
machine: Machine;
}>()
}>();
const departmentSelfServeEnabled = ref(false);
const isLoadingDepartmentSelfServeStatus = ref(false);
const isSavingDepartmentSelfServeStatus = ref(false);
const departmentSelfServeError = ref("");
const departmentId = computed(() => Number.parseInt(String(props.machine.department ?? 0), 10));
const hasDepartmentId = computed(() => Number.isInteger(departmentId.value) && departmentId.value > 0);
const departmentSelfServeStatusLabel = computed(() => (
departmentSelfServeEnabled.value ? "ENABLED" : "DISABLED"
));
const loadDepartmentSelfServeStatus = async () => {
if (!hasDepartmentId.value) {
departmentSelfServeEnabled.value = false;
departmentSelfServeError.value = "";
return;
}
isLoadingDepartmentSelfServeStatus.value = true;
departmentSelfServeError.value = "";
try {
departmentSelfServeEnabled.value = await getDepartmentSelfServeEnabled(departmentId.value);
} catch (error) {
console.error("Failed to load department self-serve status", error);
departmentSelfServeError.value = "Unable to load department self-serve status.";
} finally {
isLoadingDepartmentSelfServeStatus.value = false;
}
};
const toggleDepartmentSelfServeEnabled = async (enabled: boolean) => {
if (!hasDepartmentId.value) {
return;
}
const previousValue = departmentSelfServeEnabled.value;
departmentSelfServeEnabled.value = Boolean(enabled);
isSavingDepartmentSelfServeStatus.value = true;
departmentSelfServeError.value = "";
try {
departmentSelfServeEnabled.value = await setDepartmentSelfServeEnabled(departmentId.value, enabled);
} catch (error) {
console.error("Failed to update department self-serve status", error);
departmentSelfServeEnabled.value = previousValue;
departmentSelfServeError.value = "Unable to update department self-serve status.";
} finally {
isSavingDepartmentSelfServeStatus.value = false;
}
};
onMounted(() => {
loadDepartmentSelfServeStatus();
});
watch(
() => props.machine.department,
() => {
loadDepartmentSelfServeStatus();
}
);
</script>
@@ -30,6 +98,31 @@ defineProps<{
</div>
</template>
<template #content>
<!-- Access controls -->
<div class="divider">
<p class="divider-text">Access Controls</p>
</div>
<div class="columns is-mobile">
<div class="column is-half">
<p>Allow Self-Serve</p>
</div>
<div class="column is-half">
<div class="columns is-mobile">
<div class="column is-half">
<SelfServeMachineStatus :machineStatus="departmentSelfServeEnabled ? 'ON' : 'OFF'" />
</div>
<div class="column is-half">
<b-switch
size="is-small"
type="is-link"
:model-value="departmentSelfServeEnabled"
:disabled="!hasDepartmentId || isLoadingDepartmentSelfServeStatus || isSavingDepartmentSelfServeStatus"
@update:model-value="toggleDepartmentSelfServeEnabled"
/>
</div>
</div>
</div>
</div>
<!-- Relay controls -->
<SelfServeMachineRelayControls :machine="machine" />
<!-- Current wash cycle -->
@@ -45,4 +138,4 @@ defineProps<{
<style scoped>
</style>
</style>
@@ -0,0 +1,137 @@
// @vitest-environment jsdom
import { defineComponent, h } from "vue";
import { mount } from "@vue/test-utils";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
getDepartmentName: vi.fn(),
getDepartmentSelfServeEnabled: vi.fn(),
setDepartmentSelfServeEnabled: vi.fn(),
}));
vi.mock("@/components/pagination/departmentTabs.vue", () => ({
getDepartmentName: mocks.getDepartmentName,
}));
vi.mock("@/composables/departmentSelfServeEnabled.js", () => ({
getDepartmentSelfServeEnabled: mocks.getDepartmentSelfServeEnabled,
setDepartmentSelfServeEnabled: mocks.setDepartmentSelfServeEnabled,
}));
import SelfServeMachine from "@/views/dashboards/superUserDashboard/selfserve/displays/machine/SelfServeMachine.vue";
const flushMicrotasks = async () => {
await Promise.resolve();
await Promise.resolve();
};
const MockSwitch = defineComponent({
name: "MockSwitch",
props: {
modelValue: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
},
emits: ["update:model-value"],
setup(props, { emit }) {
return () => h("button", {
"data-testid": "department-self-serve-switch",
"data-state": props.modelValue ? "on" : "off",
"disabled": props.disabled,
onClick: () => emit("update:model-value", !props.modelValue),
});
},
});
const mountMachine = (machineOverrides = {}) => mount(SelfServeMachine, {
props: {
machine: {
id: 1,
department: 12,
name: "Lane 1",
status: "AVAILABLE",
...machineOverrides,
},
},
global: {
stubs: {
WhiteBoxCard: {
template: `
<div>
<div data-testid="header"><slot name="header" /></div>
<div data-testid="content"><slot name="content" /></div>
<div data-testid="footer"><slot name="footer" /></div>
</div>
`,
},
SelfServeMachineStatus: { template: "<div />" },
SelfServeMachineRelayControls: { template: "<div />" },
SelfServeMachineControls: { template: "<div />" },
BSwitch: MockSwitch,
"b-switch": MockSwitch,
},
},
});
describe("SelfServeMachine department self-serve management", () => {
let consoleErrorSpy;
beforeEach(() => {
mocks.getDepartmentName.mockReset();
mocks.getDepartmentSelfServeEnabled.mockReset();
mocks.setDepartmentSelfServeEnabled.mockReset();
consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
mocks.getDepartmentName.mockReturnValue("Roskilde");
mocks.getDepartmentSelfServeEnabled.mockResolvedValue(false);
mocks.setDepartmentSelfServeEnabled.mockImplementation(async (_departmentId, enabled) => enabled);
});
afterEach(() => {
consoleErrorSpy.mockRestore();
});
it("loads and displays department self-serve status", async () => {
mocks.getDepartmentSelfServeEnabled.mockResolvedValue(true);
const wrapper = mountMachine();
await flushMicrotasks();
expect(mocks.getDepartmentSelfServeEnabled).toHaveBeenCalledWith(12);
expect(wrapper.text()).toContain("Department Self-Serve");
expect(wrapper.text()).toContain("ENABLED");
expect(wrapper.get("[data-testid='department-self-serve-switch']").attributes("data-state")).toBe("on");
});
it("updates department self-serve status when toggled", async () => {
const wrapper = mountMachine();
await flushMicrotasks();
expect(wrapper.text()).toContain("DISABLED");
await wrapper.get("[data-testid='department-self-serve-switch']").trigger("click");
await flushMicrotasks();
expect(mocks.setDepartmentSelfServeEnabled).toHaveBeenCalledWith(12, true);
expect(wrapper.text()).toContain("ENABLED");
expect(wrapper.get("[data-testid='department-self-serve-switch']").attributes("data-state")).toBe("on");
});
it("reverts status and shows an error if status update fails", async () => {
mocks.setDepartmentSelfServeEnabled.mockRejectedValue(new Error("save failed"));
const wrapper = mountMachine();
await flushMicrotasks();
await wrapper.get("[data-testid='department-self-serve-switch']").trigger("click");
await flushMicrotasks();
expect(wrapper.text()).toContain("Unable to update department self-serve status.");
expect(wrapper.text()).toContain("DISABLED");
expect(wrapper.get("[data-testid='department-self-serve-switch']").attributes("data-state")).toBe("off");
});
});