From 7f77b2cf155ab3bc137733fb2ae8cd40d63be68f Mon Sep 17 00:00:00 2001
From: shamoon <4887959+shamoon@users.noreply.github.com>
Date: Tue, 3 Feb 2026 12:06:13 -0800
Subject: [PATCH] Add widget component tests (netdata..opnsense)
---
src/widgets/netdata/component.test.jsx | 59 +++++++++++++
src/widgets/nextdns/component.test.jsx | 53 ++++++++++++
src/widgets/npm/component.test.jsx | 61 +++++++++++++
src/widgets/nzbget/component.test.jsx | 61 +++++++++++++
src/widgets/octoprint/component.test.jsx | 76 ++++++++++++++++
src/widgets/ombi/component.test.jsx | 58 +++++++++++++
src/widgets/opendtu/component.test.jsx | 71 +++++++++++++++
src/widgets/openmediavault/component.test.jsx | 38 ++++++++
src/widgets/openwrt/component.test.jsx | 26 ++++++
src/widgets/opnsense/component.test.jsx | 86 +++++++++++++++++++
10 files changed, 589 insertions(+)
create mode 100644 src/widgets/netdata/component.test.jsx
create mode 100644 src/widgets/nextdns/component.test.jsx
create mode 100644 src/widgets/npm/component.test.jsx
create mode 100644 src/widgets/nzbget/component.test.jsx
create mode 100644 src/widgets/octoprint/component.test.jsx
create mode 100644 src/widgets/ombi/component.test.jsx
create mode 100644 src/widgets/opendtu/component.test.jsx
create mode 100644 src/widgets/openmediavault/component.test.jsx
create mode 100644 src/widgets/openwrt/component.test.jsx
create mode 100644 src/widgets/opnsense/component.test.jsx
diff --git a/src/widgets/netdata/component.test.jsx b/src/widgets/netdata/component.test.jsx
new file mode 100644
index 000000000..be0341a16
--- /dev/null
+++ b/src/widgets/netdata/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/netdata/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("netdata.warnings")).toBeInTheDocument();
+ expect(screen.getByText("netdata.criticals")).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 warning and critical alarm counts", () => {
+ useWidgetAPI.mockReturnValue({
+ data: { alarms: { warning: 3, critical: 1 } },
+ error: undefined,
+ });
+
+ const { container } = renderWithProviders(, {
+ settings: { hideErrors: false },
+ });
+
+ expectBlockValue(container, "netdata.warnings", 3);
+ expectBlockValue(container, "netdata.criticals", 1);
+ });
+});
diff --git a/src/widgets/nextdns/component.test.jsx b/src/widgets/nextdns/component.test.jsx
new file mode 100644
index 000000000..cdc661fb1
--- /dev/null
+++ b/src/widgets/nextdns/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/nextdns/component", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it("renders waiting status while loading", () => {
+ useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
+
+ renderWithProviders(, { settings: { hideErrors: false } });
+
+ expect(screen.getByText("widget.status")).toBeInTheDocument();
+ expect(screen.getByText("nextdns.wait")).toBeInTheDocument();
+ });
+
+ it("renders no-devices status when data array is empty", () => {
+ useWidgetAPI.mockReturnValue({ data: { data: [] }, error: undefined });
+
+ renderWithProviders(, { settings: { hideErrors: false } });
+
+ expect(screen.getByText("nextdns.no_devices")).toBeInTheDocument();
+ });
+
+ it("renders a block per device status with query counts", () => {
+ useWidgetAPI.mockReturnValue({
+ data: {
+ data: [
+ { status: "nextdns.active", queries: 10 },
+ { status: "nextdns.offline", queries: 2 },
+ ],
+ },
+ error: undefined,
+ });
+
+ renderWithProviders(, { settings: { hideErrors: false } });
+
+ expect(screen.getByText("nextdns.active")).toBeInTheDocument();
+ expect(screen.getByText("nextdns.offline")).toBeInTheDocument();
+ expect(screen.getByText("10")).toBeInTheDocument();
+ expect(screen.getByText("2")).toBeInTheDocument();
+ });
+});
diff --git a/src/widgets/npm/component.test.jsx b/src/widgets/npm/component.test.jsx
new file mode 100644
index 000000000..b8316f584
--- /dev/null
+++ b/src/widgets/npm/component.test.jsx
@@ -0,0 +1,61 @@
+// @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/npm/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("npm.enabled")).toBeInTheDocument();
+ expect(screen.getByText("npm.disabled")).toBeInTheDocument();
+ expect(screen.getByText("npm.total")).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 enabled/disabled/total host counts", () => {
+ useWidgetAPI.mockReturnValue({
+ data: [{ enabled: true }, { enabled: false }, { enabled: 1 }, { enabled: 0 }, { enabled: true }],
+ error: undefined,
+ });
+
+ const { container } = renderWithProviders(, {
+ settings: { hideErrors: false },
+ });
+
+ expectBlockValue(container, "npm.enabled", 3);
+ expectBlockValue(container, "npm.disabled", 2);
+ expectBlockValue(container, "npm.total", 5);
+ });
+});
diff --git a/src/widgets/nzbget/component.test.jsx b/src/widgets/nzbget/component.test.jsx
new file mode 100644
index 000000000..bfb076606
--- /dev/null
+++ b/src/widgets/nzbget/component.test.jsx
@@ -0,0 +1,61 @@
+// @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/nzbget/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("nzbget.rate")).toBeInTheDocument();
+ expect(screen.getByText("nzbget.remaining")).toBeInTheDocument();
+ expect(screen.getByText("nzbget.downloaded")).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 rate and sizes when loaded", () => {
+ useWidgetAPI.mockReturnValue({
+ data: { DownloadRate: 1234, RemainingSizeMB: 2, DownloadedSizeMB: 3 },
+ error: undefined,
+ });
+
+ const { container } = renderWithProviders(, {
+ settings: { hideErrors: false },
+ });
+
+ expectBlockValue(container, "nzbget.rate", 1234);
+ expectBlockValue(container, "nzbget.remaining", 2 * 1024 * 1024);
+ expectBlockValue(container, "nzbget.downloaded", 3 * 1024 * 1024);
+ });
+});
diff --git a/src/widgets/octoprint/component.test.jsx b/src/widgets/octoprint/component.test.jsx
new file mode 100644
index 000000000..e55582be4
--- /dev/null
+++ b/src/widgets/octoprint/component.test.jsx
@@ -0,0 +1,76 @@
+// @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/octoprint/component", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it("renders minimal placeholder while loading", () => {
+ useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
+
+ const { container } = renderWithProviders(, {
+ settings: { hideErrors: false },
+ });
+
+ expect(container.querySelectorAll(".service-block")).toHaveLength(1);
+ expect(screen.getByText("octoprint.printer_state")).toBeInTheDocument();
+ });
+
+ it("renders state from job_stats when printer_stats errors but job_stats is available", () => {
+ useWidgetAPI.mockImplementation((_widget, endpoint) => {
+ if (endpoint === "printer_stats") return { data: undefined, error: { message: "printer nope" } };
+ if (endpoint === "job_stats") return { data: { state: "Paused" }, error: undefined };
+ return { data: undefined, error: undefined };
+ });
+
+ const { container } = renderWithProviders(, {
+ settings: { hideErrors: false },
+ });
+
+ expectBlockValue(container, "octoprint.printer_state", "Paused");
+ expect(screen.queryByText("printer nope")).toBeNull();
+ });
+
+ it("renders job completion block when printing and completion is present", () => {
+ useWidgetAPI.mockImplementation((_widget, endpoint) => {
+ if (endpoint === "printer_stats") {
+ return {
+ data: {
+ state: { text: "Printing" },
+ temperature: { tool0: { actual: 200 }, bed: { actual: 60 } },
+ },
+ error: undefined,
+ };
+ }
+ if (endpoint === "job_stats") return { data: { progress: { completion: 12.3456 } }, error: undefined };
+ return { data: undefined, error: undefined };
+ });
+
+ const { container } = renderWithProviders(, {
+ settings: { hideErrors: false },
+ });
+
+ expect(container.querySelectorAll(".service-block")).toHaveLength(4);
+ expectBlockValue(container, "octoprint.printer_state", "Printing");
+ expectBlockValue(container, "octoprint.temp_tool", "200");
+ expectBlockValue(container, "octoprint.temp_bed", "60");
+ expectBlockValue(container, "octoprint.job_completion", "12.35%");
+ });
+});
diff --git a/src/widgets/ombi/component.test.jsx b/src/widgets/ombi/component.test.jsx
new file mode 100644
index 000000000..972aa596e
--- /dev/null
+++ b/src/widgets/ombi/component.test.jsx
@@ -0,0 +1,58 @@
+// @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/ombi/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("ombi.pending")).toBeInTheDocument();
+ expect(screen.getByText("ombi.approved")).toBeInTheDocument();
+ expect(screen.getByText("ombi.available")).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 request counts when loaded", () => {
+ useWidgetAPI.mockReturnValue({ data: { pending: 1, approved: 2, available: 3 }, error: undefined });
+
+ const { container } = renderWithProviders(, {
+ settings: { hideErrors: false },
+ });
+
+ expectBlockValue(container, "ombi.pending", 1);
+ expectBlockValue(container, "ombi.approved", 2);
+ expectBlockValue(container, "ombi.available", 3);
+ });
+});
diff --git a/src/widgets/opendtu/component.test.jsx b/src/widgets/opendtu/component.test.jsx
new file mode 100644
index 000000000..d2652986b
--- /dev/null
+++ b/src/widgets/opendtu/component.test.jsx
@@ -0,0 +1,71 @@
+// @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/opendtu/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("opendtu.yieldDay")).toBeInTheDocument();
+ expect(screen.getByText("opendtu.relativePower")).toBeInTheDocument();
+ expect(screen.getByText("opendtu.absolutePower")).toBeInTheDocument();
+ expect(screen.getByText("opendtu.limit")).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 totals and computed relative power", () => {
+ useWidgetAPI.mockReturnValue({
+ data: {
+ total: {
+ YieldDay: { v: 12.4, u: "kWh" },
+ Power: { v: 250, u: "W" },
+ },
+ inverters: [{ limit_absolute: 200 }, { limit_absolute: 300 }],
+ },
+ error: undefined,
+ });
+
+ const { container } = renderWithProviders(, {
+ settings: { hideErrors: false },
+ });
+
+ // yieldDay is rounded and has unit appended.
+ expectBlockValue(container, "opendtu.yieldDay", "12kWh");
+ // relative power is percent of power / totalLimit (250/500*100 = 50)
+ expectBlockValue(container, "opendtu.relativePower", "50");
+ expectBlockValue(container, "opendtu.absolutePower", "250W");
+ expectBlockValue(container, "opendtu.limit", "500W");
+ });
+});
diff --git a/src/widgets/openmediavault/component.test.jsx b/src/widgets/openmediavault/component.test.jsx
new file mode 100644
index 000000000..5770c32ad
--- /dev/null
+++ b/src/widgets/openmediavault/component.test.jsx
@@ -0,0 +1,38 @@
+// @vitest-environment jsdom
+
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+
+const { ServicesGetStatus, SmartGetList, DownloaderGetDownloadList } = vi.hoisted(() => ({
+ ServicesGetStatus: vi.fn(() =>
),
+ SmartGetList: vi.fn(() => ),
+ DownloaderGetDownloadList: vi.fn(() => ),
+}));
+
+vi.mock("./methods/services_get_status", () => ({ default: ServicesGetStatus }));
+vi.mock("./methods/smart_get_list", () => ({ default: SmartGetList }));
+vi.mock("./methods/downloader_get_downloadlist", () => ({ default: DownloaderGetDownloadList }));
+
+import Component from "./component";
+
+describe("widgets/openmediavault/component", () => {
+ it("routes services.getStatus method to ServicesGetStatus", () => {
+ render();
+ expect(screen.getByTestId("services.getStatus")).toBeInTheDocument();
+ });
+
+ it("routes smart.getListBg method to SmartGetList", () => {
+ render();
+ expect(screen.getByTestId("smart.getListBg")).toBeInTheDocument();
+ });
+
+ it("routes downloader.getDownloadList method to DownloaderGetDownloadList", () => {
+ render();
+ expect(screen.getByTestId("downloader.getDownloadList")).toBeInTheDocument();
+ });
+
+ it("returns null for unknown methods", () => {
+ const { container } = render();
+ expect(container.firstChild).toBeNull();
+ });
+});
diff --git a/src/widgets/openwrt/component.test.jsx b/src/widgets/openwrt/component.test.jsx
new file mode 100644
index 000000000..0b3dac69d
--- /dev/null
+++ b/src/widgets/openwrt/component.test.jsx
@@ -0,0 +1,26 @@
+// @vitest-environment jsdom
+
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+
+const { Interface, System } = vi.hoisted(() => ({
+ Interface: vi.fn(() => ),
+ System: vi.fn(() => ),
+}));
+
+vi.mock("./methods/interface", () => ({ default: Interface }));
+vi.mock("./methods/system", () => ({ default: System }));
+
+import Component from "./component";
+
+describe("widgets/openwrt/component", () => {
+ it("renders System when interfaceName is not set", () => {
+ render();
+ expect(screen.getByTestId("openwrt.system")).toBeInTheDocument();
+ });
+
+ it("renders Interface when interfaceName is set", () => {
+ render();
+ expect(screen.getByTestId("openwrt.interface")).toBeInTheDocument();
+ });
+});
diff --git a/src/widgets/opnsense/component.test.jsx b/src/widgets/opnsense/component.test.jsx
new file mode 100644
index 000000000..2fa972ce0
--- /dev/null
+++ b/src/widgets/opnsense/component.test.jsx
@@ -0,0 +1,86 @@
+// @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/opnsense/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("opnsense.cpu")).toBeInTheDocument();
+ expect(screen.getByText("opnsense.memory")).toBeInTheDocument();
+ expect(screen.getByText("opnsense.wanUpload")).toBeInTheDocument();
+ expect(screen.getByText("opnsense.wanDownload")).toBeInTheDocument();
+ });
+
+ it("renders error UI when either endpoint errors", () => {
+ useWidgetAPI.mockImplementation((_widget, endpoint) => {
+ if (endpoint === "activity") 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("parses activity headers and renders WAN rx/tx for selected interface", () => {
+ useWidgetAPI.mockImplementation((_widget, endpoint) => {
+ if (endpoint === "activity") {
+ return {
+ data: {
+ headers: ["", "", "CPU: 75.00% idle", "Mem: 123M Active, 456M Inact, 789M Wired"],
+ },
+ error: undefined,
+ };
+ }
+
+ if (endpoint === "interface") {
+ return {
+ data: {
+ interfaces: {
+ wan2: { "bytes transmitted": 1000, "bytes received": 2000 },
+ wan: { "bytes transmitted": 1, "bytes received": 2 },
+ },
+ },
+ error: undefined,
+ };
+ }
+
+ return { data: undefined, error: undefined };
+ });
+
+ const { container } = renderWithProviders(, {
+ settings: { hideErrors: false },
+ });
+
+ expectBlockValue(container, "opnsense.cpu", "25.00");
+ expectBlockValue(container, "opnsense.memory", "123M");
+ expectBlockValue(container, "opnsense.wanUpload", 1000);
+ expectBlockValue(container, "opnsense.wanDownload", 2000);
+ });
+});