Newer
Older
smart-home-server / webclient-vue / src / components / feedback / __tests__ / AppErrorState.spec.js
import { describe, it, expect, vi } from "vitest";
import { mount } from "@vue/test-utils";
import AppErrorState from "../AppErrorState.vue";

describe("AppErrorState", () => {
  it("renders title and message", () => {
    const wrapper = mount(AppErrorState, {
      props: {
        title: "Load failed",
        message: "Server error",
      },
    });

    expect(wrapper.text()).toContain("Load failed");
    expect(wrapper.text()).toContain("Server error");
  });

  it("renders retry button when retry prop provided", () => {
    const retry = vi.fn();
    const wrapper = mount(AppErrorState, {
      props: {
        title: "Error",
        retry,
      },
    });

    const btn = wrapper.find("button");
    expect(btn.exists()).toBe(true);
    expect(btn.text()).toContain("Retry");
  });

  it("does not render retry button when retry is null", () => {
    const wrapper = mount(AppErrorState, {
      props: {
        title: "Error",
        retry: null,
      },
    });

    expect(wrapper.find("button").exists()).toBe(false);
  });

  it("calls retry callback on button click", () => {
    const retry = vi.fn();
    const wrapper = mount(AppErrorState, {
      props: {
        title: "Error",
        retry,
      },
    });

    wrapper.find("button").trigger("click");

    expect(retry).toHaveBeenCalled();
  });

  it("renders error type, statusCode and errorAlias badges when error prop provided", () => {
    const wrapper = mount(AppErrorState, {
      props: {
        title: "Failed",
        error: {
          type: "api_error",
          message: "Invalid input",
          statusCode: 422,
          errorAlias: "VALIDATION_FAILED",
          raw: { status: false },
        },
      },
    });

    expect(wrapper.text()).toContain("api_error");
    expect(wrapper.text()).toContain("HTTP 422");
    expect(wrapper.text()).toContain("VALIDATION_FAILED");
  });

  it("shows copy button when error prop is provided", () => {
    const wrapper = mount(AppErrorState, {
      props: {
        title: "Failed",
        error: {
          type: "network_error",
          message: "Connection refused",
        },
      },
    });

    const copyBtn = wrapper.find('button[aria-label="Copy error details"]');
    expect(copyBtn.exists()).toBe(true);
  });

  it("hides copy button when no error prop is provided", () => {
    const wrapper = mount(AppErrorState, {
      props: {
        title: "Failed",
        retry: null,
      },
    });

    const copyBtn = wrapper.find('button[aria-label="Copy error details"]');
    expect(copyBtn.exists()).toBe(false);
  });

  it("formats error details as JSON for copying", async () => {
    Object.assign(navigator, {
      clipboard: { writeText: vi.fn().mockResolvedValue(undefined) },
    });

    const error = {
      type: "api_error",
      message: "Invalid input",
      statusCode: 422,
      raw: { status: false },
    };

    const wrapper = mount(AppErrorState, {
      props: { title: "Failed", error },
    });

    const copyBtn = wrapper.find('button[aria-label="Copy error details"]');
    expect(copyBtn.exists()).toBe(true);

    await copyBtn.trigger("click");

    expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
      JSON.stringify(error, null, 2)
    );
  });
});