diff --git a/webclient-vue/src/components/script/ActionScriptsGrid.vue b/webclient-vue/src/components/script/ActionScriptsGrid.vue index 43a80fa..ef9f74f 100644 --- a/webclient-vue/src/components/script/ActionScriptsGrid.vue +++ b/webclient-vue/src/components/script/ActionScriptsGrid.vue @@ -54,6 +54,8 @@ }, }); +const emit = defineEmits(["run-success"]); + const router = useRouter(); const scriptsStore = useScriptsStore(); const areasStore = useAreasStore(); @@ -71,6 +73,7 @@ title: `Ran ${alias}`, text: scriptsStore.lastRunResult?.execTime ? `Exec time: ${scriptsStore.lastRunResult.execTime}` : undefined, }); + emit("run-success", { alias }); } else { toast.error({ title: `Failed ${alias}`, text: result?.error?.message || "Unknown error" }); } diff --git a/webclient-vue/src/components/script/__tests__/ActionScriptsGrid.spec.js b/webclient-vue/src/components/script/__tests__/ActionScriptsGrid.spec.js index a3b0953..027596a 100644 --- a/webclient-vue/src/components/script/__tests__/ActionScriptsGrid.spec.js +++ b/webclient-vue/src/components/script/__tests__/ActionScriptsGrid.spec.js @@ -2,8 +2,21 @@ import { mount } from "@vue/test-utils"; import { setActivePinia, createPinia } from "pinia"; import { useAreasStore } from "../../../stores/areas.js"; +import { useScriptsStore } from "../../../stores/scripts.js"; import ActionScriptsGrid from "../ActionScriptsGrid.vue"; +vi.mock("gnexus-ui-kit/vue", async () => { + const actual = await vi.importActual("gnexus-ui-kit/vue"); + return { + ...actual, + useToast: () => ({ success: vi.fn(), error: vi.fn() }), + }; +}); + +vi.mock("vue-router", () => ({ + useRouter: () => ({ push: vi.fn() }), +})); + describe("ActionScriptsGrid", () => { beforeEach(() => { setActivePinia(createPinia()); @@ -66,9 +79,33 @@ expect(wrapper.text()).toContain("Test"); }); - it("emits run event on button click", async () => { + it("emits run-success after successful run", async () => { + const scriptsStore = useScriptsStore(); + vi.spyOn(scriptsStore, "runScript").mockResolvedValue({ ok: true }); + scriptsStore.lastRunResult = { execTime: "0.042 seconds" }; + const wrapper = createWrapper(); - const buttons = wrapper.findAll("button"); + const buttons = wrapper.findAll("button").filter((b) => !b.attributes("disabled")); expect(buttons.length).toBeGreaterThan(0); + + await buttons[0].trigger("click"); + await new Promise((r) => setTimeout(r, 0)); // flush microtasks + + expect(wrapper.emitted("run-success")).toBeTruthy(); + expect(wrapper.emitted("run-success")[0]).toEqual([{ alias: "kitchen_light" }]); + }); + + it("does not emit run-success on failed run", async () => { + const scriptsStore = useScriptsStore(); + vi.spyOn(scriptsStore, "runScript").mockResolvedValue({ ok: false, error: { message: "Boom" } }); + + const wrapper = createWrapper(); + const buttons = wrapper.findAll("button").filter((b) => !b.attributes("disabled")); + expect(buttons.length).toBeGreaterThan(0); + + await buttons[0].trigger("click"); + await new Promise((r) => setTimeout(r, 0)); + + expect(wrapper.emitted("run-success")).toBeFalsy(); }); }); diff --git a/webclient-vue/src/features/areas/pages/AreaDetailPage.vue b/webclient-vue/src/features/areas/pages/AreaDetailPage.vue index f5c7f98..f70eb86 100644 --- a/webclient-vue/src/features/areas/pages/AreaDetailPage.vue +++ b/webclient-vue/src/features/areas/pages/AreaDetailPage.vue @@ -36,7 +36,7 @@ title="No actions" message="No action scripts assigned to this area." /> - +
@@ -396,6 +396,12 @@ toast.success({ title: "Unassigned", text: "Area unassigned from parent successfully" }); } +async function refreshDeviceStates() { + if (areasStore.currentAreaDevices.length > 0) { + await devicesStore.loadStatesFor(areasStore.currentAreaDevices); + } +} + async function init() { const id = route.params.id; if (!id) return; @@ -406,9 +412,7 @@ if (areasStore.areasById[String(id)]) { await areasStore.loadAreaDetail(id); - if (areasStore.currentAreaDevices.length > 0) { - await devicesStore.loadStatesFor(areasStore.currentAreaDevices); - } + await refreshDeviceStates(); } }