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:
+98
-5
@@ -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");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user