From a262e7ec5cf2cb77831bd95b0e8b6eda6a5bfbac Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Tue, 3 Feb 2026 11:47:56 -0800 Subject: [PATCH] Add widget component tests (medusa..netalertx) --- src/widgets/medusa/component.test.jsx | 81 +++++++++++++++++++++ src/widgets/mikrotik/component.test.jsx | 82 +++++++++++++++++++++ src/widgets/minecraft/component.test.jsx | 65 +++++++++++++++++ src/widgets/miniflux/component.test.jsx | 56 +++++++++++++++ src/widgets/mjpeg/component.test.jsx | 29 ++++++++ src/widgets/moonraker/component.test.jsx | 90 ++++++++++++++++++++++++ src/widgets/mylar/component.test.jsx | 68 ++++++++++++++++++ src/widgets/myspeed/component.test.jsx | 59 ++++++++++++++++ src/widgets/navidrome/component.test.jsx | 53 ++++++++++++++ src/widgets/netalertx/component.test.jsx | 55 +++++++++++++++ 10 files changed, 638 insertions(+) create mode 100644 src/widgets/medusa/component.test.jsx create mode 100644 src/widgets/mikrotik/component.test.jsx create mode 100644 src/widgets/minecraft/component.test.jsx create mode 100644 src/widgets/miniflux/component.test.jsx create mode 100644 src/widgets/mjpeg/component.test.jsx create mode 100644 src/widgets/moonraker/component.test.jsx create mode 100644 src/widgets/mylar/component.test.jsx create mode 100644 src/widgets/myspeed/component.test.jsx create mode 100644 src/widgets/navidrome/component.test.jsx create mode 100644 src/widgets/netalertx/component.test.jsx diff --git a/src/widgets/medusa/component.test.jsx b/src/widgets/medusa/component.test.jsx new file mode 100644 index 000000000..ae6bfa752 --- /dev/null +++ b/src/widgets/medusa/component.test.jsx @@ -0,0 +1,81 @@ +// @vitest-environment jsdom + +import { screen } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { renderWithProviders } from "test-utils/render-with-providers"; +import { findServiceBlockByLabel } from "test-utils/widget-assertions"; + +const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() })); +vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI })); + +import Component from "./component"; + +function expectBlockValue(container, label, value) { + const block = findServiceBlockByLabel(container, label); + expect(block, `missing block for ${label}`).toBeTruthy(); + expect(block.textContent).toContain(String(value)); +} + +describe("widgets/medusa/component", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("renders placeholders while loading", () => { + useWidgetAPI.mockReturnValue({ data: undefined, error: undefined }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expect(container.querySelectorAll(".service-block")).toHaveLength(3); + expect(screen.getByText("medusa.wanted")).toBeInTheDocument(); + expect(screen.getByText("medusa.queued")).toBeInTheDocument(); + expect(screen.getByText("medusa.series")).toBeInTheDocument(); + }); + + it("renders error UI when either endpoint errors", () => { + useWidgetAPI.mockImplementation((_widget, endpoint) => { + if (endpoint === "future") return { data: undefined, error: { message: "nope" } }; + return { data: undefined, error: undefined }; + }); + + renderWithProviders(, { settings: { hideErrors: false } }); + + expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0); + expect(screen.getByText("nope")).toBeInTheDocument(); + }); + + it("computes wanted total from future lists and renders stats", () => { + useWidgetAPI.mockImplementation((_widget, endpoint) => { + if (endpoint === "future") { + return { + data: { + data: { + later: [{ id: 1 }], + missed: [{ id: 2 }, { id: 3 }], + soon: [], + today: [{ id: 4 }, { id: 5 }, { id: 6 }], + }, + }, + error: undefined, + }; + } + + if (endpoint === "stats") { + return { data: { data: { ep_snatched: 7, shows_active: 8 } }, error: undefined }; + } + + return { data: undefined, error: undefined }; + }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expectBlockValue(container, "medusa.wanted", 6); + expectBlockValue(container, "medusa.queued", 7); + expectBlockValue(container, "medusa.series", 8); + }); +}); diff --git a/src/widgets/mikrotik/component.test.jsx b/src/widgets/mikrotik/component.test.jsx new file mode 100644 index 000000000..87f4af44c --- /dev/null +++ b/src/widgets/mikrotik/component.test.jsx @@ -0,0 +1,82 @@ +// @vitest-environment jsdom + +import { screen } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { renderWithProviders } from "test-utils/render-with-providers"; +import { findServiceBlockByLabel } from "test-utils/widget-assertions"; + +const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() })); +vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI })); + +import Component from "./component"; + +function expectBlockValue(container, label, value) { + const block = findServiceBlockByLabel(container, label); + expect(block, `missing block for ${label}`).toBeTruthy(); + expect(block.textContent).toContain(String(value)); +} + +describe("widgets/mikrotik/component", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("renders placeholders while loading", () => { + useWidgetAPI.mockReturnValue({ data: undefined, error: undefined }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expect(container.querySelectorAll(".service-block")).toHaveLength(4); + expect(screen.getByText("mikrotik.uptime")).toBeInTheDocument(); + expect(screen.getByText("mikrotik.cpuLoad")).toBeInTheDocument(); + expect(screen.getByText("mikrotik.memoryUsed")).toBeInTheDocument(); + expect(screen.getByText("mikrotik.numberOfLeases")).toBeInTheDocument(); + }); + + it("renders error UI when either endpoint errors", () => { + useWidgetAPI.mockImplementation((_widget, endpoint) => { + if (endpoint === "leases") return { data: undefined, error: { message: "nope" } }; + return { data: undefined, error: undefined }; + }); + + renderWithProviders(, { settings: { hideErrors: false } }); + + expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0); + expect(screen.getByText("nope")).toBeInTheDocument(); + }); + + it("renders uptime, cpu load, memory used, and lease count", () => { + useWidgetAPI.mockImplementation((_widget, endpoint) => { + if (endpoint === "system") { + return { + data: { + uptime: "1d", + "cpu-load": 10, + "free-memory": 25, + "total-memory": 100, + }, + error: undefined, + }; + } + + if (endpoint === "leases") { + return { data: [{ id: 1 }, { id: 2 }, { id: 3 }], error: undefined }; + } + + return { data: undefined, error: undefined }; + }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + // memoryUsed = 100 - (25/100)*100 = 75 + expectBlockValue(container, "mikrotik.uptime", "1d"); + expectBlockValue(container, "mikrotik.cpuLoad", 10); + expectBlockValue(container, "mikrotik.memoryUsed", 75); + expectBlockValue(container, "mikrotik.numberOfLeases", 3); + }); +}); diff --git a/src/widgets/minecraft/component.test.jsx b/src/widgets/minecraft/component.test.jsx new file mode 100644 index 000000000..399056232 --- /dev/null +++ b/src/widgets/minecraft/component.test.jsx @@ -0,0 +1,65 @@ +// @vitest-environment jsdom + +import { screen } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { renderWithProviders } from "test-utils/render-with-providers"; +import { findServiceBlockByLabel } from "test-utils/widget-assertions"; + +const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() })); +vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI })); + +import Component from "./component"; + +function expectBlockValue(container, label, value) { + const block = findServiceBlockByLabel(container, label); + expect(block, `missing block for ${label}`).toBeTruthy(); + expect(block.textContent).toContain(String(value)); +} + +describe("widgets/minecraft/component", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("renders placeholders while loading", () => { + useWidgetAPI.mockReturnValue({ data: undefined, error: undefined }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expect(container.querySelectorAll(".service-block")).toHaveLength(3); + expect(screen.getByText("minecraft.status")).toBeInTheDocument(); + expect(screen.getByText("minecraft.players")).toBeInTheDocument(); + expect(screen.getByText("minecraft.version")).toBeInTheDocument(); + }); + + it("renders error UI when status endpoint errors", () => { + useWidgetAPI.mockReturnValue({ data: undefined, error: { message: "nope" } }); + + renderWithProviders(, { settings: { hideErrors: false } }); + + expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0); + expect(screen.getByText("nope")).toBeInTheDocument(); + }); + + it("renders status, players, and version when loaded", () => { + useWidgetAPI.mockReturnValue({ + data: { + online: true, + players: { online: 2, max: 10 }, + version: "1.20.1", + }, + error: undefined, + }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expectBlockValue(container, "minecraft.status", "minecraft.up"); + expectBlockValue(container, "minecraft.players", "2 / 10"); + expectBlockValue(container, "minecraft.version", "1.20.1"); + }); +}); diff --git a/src/widgets/miniflux/component.test.jsx b/src/widgets/miniflux/component.test.jsx new file mode 100644 index 000000000..0a3ec4d1c --- /dev/null +++ b/src/widgets/miniflux/component.test.jsx @@ -0,0 +1,56 @@ +// @vitest-environment jsdom + +import { screen } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { renderWithProviders } from "test-utils/render-with-providers"; +import { findServiceBlockByLabel } from "test-utils/widget-assertions"; + +const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() })); +vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI })); + +import Component from "./component"; + +function expectBlockValue(container, label, value) { + const block = findServiceBlockByLabel(container, label); + expect(block, `missing block for ${label}`).toBeTruthy(); + expect(block.textContent).toContain(String(value)); +} + +describe("widgets/miniflux/component", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("renders placeholders while loading", () => { + useWidgetAPI.mockReturnValue({ data: undefined, error: undefined }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expect(container.querySelectorAll(".service-block")).toHaveLength(2); + expect(screen.getByText("miniflux.unread")).toBeInTheDocument(); + expect(screen.getByText("miniflux.read")).toBeInTheDocument(); + }); + + it("renders error UI when counters endpoint errors", () => { + useWidgetAPI.mockReturnValue({ data: undefined, error: { message: "nope" } }); + + renderWithProviders(, { settings: { hideErrors: false } }); + + expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0); + expect(screen.getByText("nope")).toBeInTheDocument(); + }); + + it("renders unread and read counters when loaded", () => { + useWidgetAPI.mockReturnValue({ data: { unread: 3, read: 7 }, error: undefined }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expectBlockValue(container, "miniflux.unread", 3); + expectBlockValue(container, "miniflux.read", 7); + }); +}); diff --git a/src/widgets/mjpeg/component.test.jsx b/src/widgets/mjpeg/component.test.jsx new file mode 100644 index 000000000..1246d9b89 --- /dev/null +++ b/src/widgets/mjpeg/component.test.jsx @@ -0,0 +1,29 @@ +// @vitest-environment jsdom + +import { render, screen } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; + +// next/image requires Next runtime features; stub it for component tests. +vi.mock("next/image", () => ({ + default: (props) => { + const { src, alt, objectFit, className, onError } = props; + return {alt}; + }, +})); + +import Component from "./component"; + +describe("widgets/mjpeg/component", () => { + it("renders the stream images", () => { + render(); + + const imgs = screen.getAllByAltText("stream"); + expect(imgs).toHaveLength(2); + expect(imgs[0].getAttribute("src")).toBe("http://example/stream.jpg"); + expect(imgs[1].getAttribute("src")).toBe("http://example/stream.jpg"); + + // Both renders pass through objectFit; the first is "fill", the second uses widget.fit. + expect(imgs[0].getAttribute("data-object-fit")).toBe("fill"); + expect(imgs[1].getAttribute("data-object-fit")).toBe("cover"); + }); +}); diff --git a/src/widgets/moonraker/component.test.jsx b/src/widgets/moonraker/component.test.jsx new file mode 100644 index 000000000..dab81e317 --- /dev/null +++ b/src/widgets/moonraker/component.test.jsx @@ -0,0 +1,90 @@ +// @vitest-environment jsdom + +import { screen } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { renderWithProviders } from "test-utils/render-with-providers"; +import { findServiceBlockByLabel } from "test-utils/widget-assertions"; + +const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() })); +vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI })); + +import Component from "./component"; + +function expectBlockValue(container, label, value) { + const block = findServiceBlockByLabel(container, label); + expect(block, `missing block for ${label}`).toBeTruthy(); + expect(block.textContent).toContain(String(value)); +} + +describe("widgets/moonraker/component", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("renders placeholders while loading", () => { + useWidgetAPI.mockReturnValue({ data: undefined, error: undefined }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expect(container.querySelectorAll(".service-block")).toHaveLength(1); + expect(screen.getByText("moonraker.printer_state")).toBeInTheDocument(); + }); + + it("renders printer state as shutdown when webhook reports shutdown", () => { + useWidgetAPI.mockImplementation((_widget, endpoint) => { + if (endpoint === "print_stats") { + return { data: { result: { status: { print_stats: { state: "standby", info: {} } } } }, error: undefined }; + } + if (endpoint === "display_status") { + return { data: { result: { status: { display_status: { progress: 0 } } } }, error: undefined }; + } + if (endpoint === "webhooks") { + return { data: { result: { status: { webhooks: { state: "shutdown" } } } }, error: undefined }; + } + return { data: undefined, error: undefined }; + }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expect(container.querySelectorAll(".service-block")).toHaveLength(1); + expectBlockValue(container, "moonraker.printer_state", "shutdown"); + }); + + it("renders layers, progress and print status when active", () => { + useWidgetAPI.mockImplementation((_widget, endpoint) => { + if (endpoint === "print_stats") { + return { + data: { + result: { + status: { + print_stats: { state: "printing", info: { current_layer: 1, total_layer: 2 } }, + }, + }, + }, + error: undefined, + }; + } + if (endpoint === "display_status") { + return { data: { result: { status: { display_status: { progress: 0.25 } } } }, error: undefined }; + } + if (endpoint === "webhooks") { + return { data: { result: { status: { webhooks: { state: "ready" } } } }, error: undefined }; + } + return { data: undefined, error: undefined }; + }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expect(container.querySelectorAll(".service-block")).toHaveLength(3); + expectBlockValue(container, "moonraker.layers", "1 / 2"); + expectBlockValue(container, "moonraker.print_progress", 25); + expectBlockValue(container, "moonraker.print_status", "printing"); + }); +}); diff --git a/src/widgets/mylar/component.test.jsx b/src/widgets/mylar/component.test.jsx new file mode 100644 index 000000000..4e7846525 --- /dev/null +++ b/src/widgets/mylar/component.test.jsx @@ -0,0 +1,68 @@ +// @vitest-environment jsdom + +import { screen } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { renderWithProviders } from "test-utils/render-with-providers"; +import { findServiceBlockByLabel } from "test-utils/widget-assertions"; + +const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() })); +vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI })); + +import Component from "./component"; + +function expectBlockValue(container, label, value) { + const block = findServiceBlockByLabel(container, label); + expect(block, `missing block for ${label}`).toBeTruthy(); + expect(block.textContent).toContain(String(value)); +} + +describe("widgets/mylar/component", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("renders placeholders while loading", () => { + useWidgetAPI.mockReturnValue({ data: undefined, error: undefined }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expect(container.querySelectorAll(".service-block")).toHaveLength(3); + expect(screen.getByText("mylar.series")).toBeInTheDocument(); + expect(screen.getByText("mylar.issues")).toBeInTheDocument(); + expect(screen.getByText("mylar.wanted")).toBeInTheDocument(); + }); + + it("renders error UI when any endpoint errors", () => { + useWidgetAPI.mockImplementation((_widget, endpoint) => { + if (endpoint === "issues") return { data: undefined, error: { message: "nope" } }; + return { data: undefined, error: undefined }; + }); + + renderWithProviders(, { settings: { hideErrors: false } }); + + expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0); + expect(screen.getByText("nope")).toBeInTheDocument(); + }); + + it("renders series count, total issues, and wanted issues", () => { + useWidgetAPI.mockImplementation((_widget, endpoint) => { + if (endpoint === "series") return { data: { data: [{ id: 1 }, { id: 2 }] }, error: undefined }; + if (endpoint === "issues") { + return { data: { data: [{ totalIssues: 3 }, { totalIssues: 4 }] }, error: undefined }; + } + if (endpoint === "wanted") return { data: { issues: [{ id: 1 }] }, error: undefined }; + return { data: undefined, error: undefined }; + }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expectBlockValue(container, "mylar.series", 2); + expectBlockValue(container, "mylar.issues", 7); + expectBlockValue(container, "mylar.wanted", 1); + }); +}); diff --git a/src/widgets/myspeed/component.test.jsx b/src/widgets/myspeed/component.test.jsx new file mode 100644 index 000000000..5bc5fb3b5 --- /dev/null +++ b/src/widgets/myspeed/component.test.jsx @@ -0,0 +1,59 @@ +// @vitest-environment jsdom + +import { screen } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { renderWithProviders } from "test-utils/render-with-providers"; +import { findServiceBlockByLabel } from "test-utils/widget-assertions"; + +const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() })); +vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI })); + +import Component from "./component"; + +function expectBlockValue(container, label, value) { + const block = findServiceBlockByLabel(container, label); + expect(block, `missing block for ${label}`).toBeTruthy(); + expect(block.textContent).toContain(String(value)); +} + +describe("widgets/myspeed/component", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("renders placeholders while loading", () => { + useWidgetAPI.mockReturnValue({ data: undefined, error: undefined }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expect(container.querySelectorAll(".service-block")).toHaveLength(3); + expect(screen.getByText("myspeed.download")).toBeInTheDocument(); + expect(screen.getByText("myspeed.upload")).toBeInTheDocument(); + expect(screen.getByText("myspeed.ping")).toBeInTheDocument(); + }); + + it("renders error UI when endpoint errors", () => { + useWidgetAPI.mockReturnValue({ data: undefined, error: { message: "nope" } }); + + renderWithProviders(, { settings: { hideErrors: false } }); + + expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0); + expect(screen.getByText("nope")).toBeInTheDocument(); + }); + + it("renders download, upload and ping when loaded", () => { + useWidgetAPI.mockReturnValue({ data: [{ download: 1, upload: 2, ping: 3 }], error: undefined }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + // t("common.bitrate") returns the raw value from setup; widget multiplies by 1e6. + expectBlockValue(container, "myspeed.download", 1000 * 1000); + expectBlockValue(container, "myspeed.upload", 2 * 1000 * 1000); + expectBlockValue(container, "myspeed.ping", 3); + }); +}); diff --git a/src/widgets/navidrome/component.test.jsx b/src/widgets/navidrome/component.test.jsx new file mode 100644 index 000000000..064b92742 --- /dev/null +++ b/src/widgets/navidrome/component.test.jsx @@ -0,0 +1,53 @@ +// @vitest-environment jsdom + +import { screen } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { renderWithProviders } from "test-utils/render-with-providers"; + +const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() })); +vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI })); + +import Component from "./component"; + +describe("widgets/navidrome/component", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("renders a waiting row while loading", () => { + useWidgetAPI.mockReturnValue({ data: undefined, error: undefined }); + + renderWithProviders(, { settings: { hideErrors: false } }); + + expect(screen.getByText("navidrome.please_wait")).toBeInTheDocument(); + }); + + it("renders an error container when the API errors", () => { + useWidgetAPI.mockReturnValue({ data: undefined, error: { message: "nope" } }); + + renderWithProviders(, { settings: { hideErrors: false } }); + + expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0); + expect(screen.getByText("nope")).toBeInTheDocument(); + }); + + it("renders now playing entries when present", () => { + useWidgetAPI.mockReturnValue({ + data: { + "subsonic-response": { + nowPlaying: { + entry: { + 0: { id: "a", title: "Song", artist: "Artist", album: "Album", username: "user" }, + }, + }, + }, + }, + error: undefined, + }); + + renderWithProviders(, { settings: { hideErrors: false } }); + + expect(screen.getByText("Artist - Song — Album (user)")).toBeInTheDocument(); + }); +}); diff --git a/src/widgets/netalertx/component.test.jsx b/src/widgets/netalertx/component.test.jsx new file mode 100644 index 000000000..a4b077dfc --- /dev/null +++ b/src/widgets/netalertx/component.test.jsx @@ -0,0 +1,55 @@ +// @vitest-environment jsdom + +import { screen } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { renderWithProviders } from "test-utils/render-with-providers"; +import { findServiceBlockByLabel } from "test-utils/widget-assertions"; + +const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() })); +vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI })); + +import Component from "./component"; + +function expectBlockValue(container, label, value) { + const block = findServiceBlockByLabel(container, label); + expect(block, `missing block for ${label}`).toBeTruthy(); + expect(block.textContent).toContain(String(value)); +} + +describe("widgets/netalertx/component", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("renders placeholders while loading", () => { + useWidgetAPI.mockReturnValue({ data: undefined, error: undefined }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expect(container.querySelectorAll(".service-block")).toHaveLength(4); + expect(screen.getByText("netalertx.total")).toBeInTheDocument(); + expect(screen.getByText("netalertx.connected")).toBeInTheDocument(); + expect(screen.getByText("netalertx.new_devices")).toBeInTheDocument(); + expect(screen.getByText("netalertx.down_alerts")).toBeInTheDocument(); + }); + + it("uses datav2 endpoint for version > 1 and renders parsed totals", () => { + useWidgetAPI.mockImplementation((_widget, endpoint) => { + if (endpoint === "datav2") return { data: ["10", "5", "0", "2", "1"], error: undefined }; + return { data: undefined, error: undefined }; + }); + + const { container } = renderWithProviders(, { + settings: { hideErrors: false }, + }); + + expect(useWidgetAPI).toHaveBeenCalled(); + expectBlockValue(container, "netalertx.total", 10); + expectBlockValue(container, "netalertx.connected", 5); + expectBlockValue(container, "netalertx.new_devices", 2); + expectBlockValue(container, "netalertx.down_alerts", 1); + }); +});