Newer
Older
smart-home-server / webclient / src / components / script / __tests__ / ActionScriptsGrid.spec.js
import { describe, it, expect, beforeEach, vi } from "vitest";
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());
  });

  function createWrapper(props = {}) {
    return mount(ActionScriptsGrid, {
      props: {
        scripts: [
          { id: 1, alias: "kitchen_light", name: "Kitchen Light", icon: '<i class="ph ph-lightbulb"></i>', description: "Toggle kitchen", state: "enabled", author: "Test", scope: "KitchenScope", area_id: 1 },
          { id: 2, alias: "hall_light", name: "Hall Light", state: "disabled", author: "Test" },
        ],
        ...props,
      },
    });
  }

  it("renders action cards for each script", () => {
    const wrapper = createWrapper();
    expect(wrapper.text()).toContain("Kitchen Light");
    expect(wrapper.text()).toContain("Hall Light");
  });

  it("shows script description when present", () => {
    const wrapper = createWrapper();
    expect(wrapper.text()).toContain("Toggle kitchen");
  });

  it("shows state badge with success variant for enabled", () => {
    const wrapper = createWrapper();
    expect(wrapper.text()).toContain("enabled");
  });

  it("shows state badge with secondary variant for disabled", () => {
    const wrapper = createWrapper();
    expect(wrapper.text()).toContain("disabled");
  });

  it("shows scope badge when present", () => {
    const wrapper = createWrapper();
    expect(wrapper.text()).toContain("KitchenScope");
  });

  it("shows area badge when showAreaBadge is true and area exists", () => {
    const areasStore = useAreasStore();
    areasStore.areas = [{ id: 1, display_name: "Kitchen Area" }];
    const wrapper = createWrapper({ showAreaBadge: true });
    expect(wrapper.text()).toContain("Kitchen Area");
  });

  it("does not show area badge when showAreaBadge is false", () => {
    const areasStore = useAreasStore();
    areasStore.areas = [{ id: 1, display_name: "Kitchen Area" }];
    const wrapper = createWrapper({ showAreaBadge: false });
    expect(wrapper.text()).not.toContain("Kitchen Area");
  });

  it("shows author when present", () => {
    const wrapper = createWrapper();
    expect(wrapper.text()).toContain("Test");
  });

  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").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();
  });
});