mirror of
https://github.com/gethomepage/homepage.git
synced 2026-02-08 00:40:52 +08:00
Test: 10 more widget components (A)
This commit is contained in:
63
src/widgets/adguard/component.test.jsx
Normal file
63
src/widgets/adguard/component.test.jsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// @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/adguard/component", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders placeholders while loading", () => {
|
||||||
|
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "adguard" } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
expect(screen.getByText("adguard.queries")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("adguard.blocked")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("adguard.filtered")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("adguard.latency")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders error UI when widget API errors", () => {
|
||||||
|
useWidgetAPI.mockReturnValue({ data: undefined, error: { message: "nope" } });
|
||||||
|
|
||||||
|
renderWithProviders(<Component service={{ widget: { type: "adguard" } }} />, { settings: { hideErrors: false } });
|
||||||
|
|
||||||
|
expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders computed filtered and latency values", () => {
|
||||||
|
useWidgetAPI.mockReturnValue({
|
||||||
|
data: {
|
||||||
|
num_dns_queries: 100,
|
||||||
|
num_blocked_filtering: 20,
|
||||||
|
num_replaced_safebrowsing: 1,
|
||||||
|
num_replaced_safesearch: 2,
|
||||||
|
num_replaced_parental: 3,
|
||||||
|
avg_processing_time: 0.01,
|
||||||
|
},
|
||||||
|
error: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
renderWithProviders(<Component service={{ widget: { type: "adguard" } }} />, { settings: { hideErrors: false } });
|
||||||
|
|
||||||
|
expect(screen.getByText("100")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("20")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("6")).toBeInTheDocument(); // filtered sum
|
||||||
|
expect(screen.getByText("10")).toBeInTheDocument(); // 0.01s -> 10ms
|
||||||
|
});
|
||||||
|
});
|
||||||
48
src/widgets/apcups/component.test.jsx
Normal file
48
src/widgets/apcups/component.test.jsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// @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/apcups/component", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders placeholders while loading", () => {
|
||||||
|
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "apcups" } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
expect(screen.getByText("apcups.status")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("apcups.load")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("apcups.bcharge")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("apcups.timeleft")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders values when loaded", () => {
|
||||||
|
useWidgetAPI.mockReturnValue({
|
||||||
|
data: { status: "ONLINE", load: "12", bcharge: "99", timeleft: "30" },
|
||||||
|
error: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
renderWithProviders(<Component service={{ widget: { type: "apcups" } }} />, { settings: { hideErrors: false } });
|
||||||
|
|
||||||
|
expect(screen.getByText("ONLINE")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("12")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("99")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("30")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
65
src/widgets/argocd/component.test.jsx
Normal file
65
src/widgets/argocd/component.test.jsx
Normal file
@@ -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";
|
||||||
|
|
||||||
|
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 blocks = Array.from(container.querySelectorAll(".service-block"));
|
||||||
|
const block = blocks.find((b) => b.textContent?.includes(label));
|
||||||
|
expect(block, `missing block for ${label}`).toBeTruthy();
|
||||||
|
expect(block.textContent).toContain(String(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("widgets/argocd/component", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defaults and truncates widget.fields to 4 and renders placeholders while loading", () => {
|
||||||
|
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||||
|
|
||||||
|
const service = { widget: { type: "argocd", fields: ["apps", "synced", "outOfSync", "healthy", "extra"] } };
|
||||||
|
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||||
|
|
||||||
|
expect(service.widget.fields).toEqual(["apps", "synced", "outOfSync", "healthy"]);
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
expect(screen.getByText("argocd.apps")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("argocd.synced")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("argocd.outOfSync")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("argocd.healthy")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders counts when loaded", () => {
|
||||||
|
useWidgetAPI.mockReturnValue({
|
||||||
|
data: {
|
||||||
|
items: [
|
||||||
|
{ status: { sync: { status: "Synced" }, health: { status: "Healthy" } } },
|
||||||
|
{ status: { sync: { status: "OutOfSync" }, health: { status: "Degraded" } } },
|
||||||
|
{ status: { sync: { status: "Synced" }, health: { status: "Healthy" } } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
error: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const service = { widget: { type: "argocd" } };
|
||||||
|
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||||
|
|
||||||
|
// Default widget fields: apps/synced/outOfSync/healthy => all 4 should be visible.
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
|
||||||
|
expectBlockValue(container, "argocd.apps", 3);
|
||||||
|
expectBlockValue(container, "argocd.synced", 2);
|
||||||
|
expectBlockValue(container, "argocd.outOfSync", 1);
|
||||||
|
expectBlockValue(container, "argocd.healthy", 2);
|
||||||
|
});
|
||||||
|
});
|
||||||
51
src/widgets/atsumeru/component.test.jsx
Normal file
51
src/widgets/atsumeru/component.test.jsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// @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/atsumeru/component", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders placeholders while loading", () => {
|
||||||
|
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "atsumeru" } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
expect(screen.getByText("atsumeru.series")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("atsumeru.archives")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("atsumeru.chapters")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("atsumeru.categories")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders values when loaded", () => {
|
||||||
|
useWidgetAPI.mockReturnValue({
|
||||||
|
data: { stats: { total_series: 1, total_archives: 2, total_chapters: 3, total_categories: 4 } },
|
||||||
|
error: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "atsumeru" } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
expect(screen.getByText("1")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("2")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("3")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("4")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
62
src/widgets/audiobookshelf/component.test.jsx
Normal file
62
src/widgets/audiobookshelf/component.test.jsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// @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";
|
||||||
|
|
||||||
|
function expectBlockValue(container, label, value) {
|
||||||
|
const blocks = Array.from(container.querySelectorAll(".service-block"));
|
||||||
|
const block = blocks.find((b) => b.textContent?.includes(label));
|
||||||
|
expect(block, `missing block for ${label}`).toBeTruthy();
|
||||||
|
expect(block.textContent).toContain(String(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("widgets/audiobookshelf/component", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders placeholders while loading", () => {
|
||||||
|
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "audiobookshelf" } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
expect(screen.getByText("audiobookshelf.podcasts")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("audiobookshelf.podcastsDuration")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("audiobookshelf.books")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("audiobookshelf.booksDuration")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("aggregates totals across libraries by mediaType", () => {
|
||||||
|
useWidgetAPI.mockReturnValue({
|
||||||
|
data: [
|
||||||
|
{ mediaType: "podcast", stats: { totalItems: "2", totalDuration: "100" } },
|
||||||
|
{ mediaType: "podcast", stats: { totalItems: "1", totalDuration: "200" } },
|
||||||
|
{ mediaType: "book", stats: { totalItems: "4", totalDuration: "300" } },
|
||||||
|
],
|
||||||
|
error: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "audiobookshelf" } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
expectBlockValue(container, "audiobookshelf.podcasts", 3);
|
||||||
|
expectBlockValue(container, "audiobookshelf.podcastsDuration", 300);
|
||||||
|
expectBlockValue(container, "audiobookshelf.books", 4);
|
||||||
|
expectBlockValue(container, "audiobookshelf.booksDuration", 300);
|
||||||
|
});
|
||||||
|
});
|
||||||
108
src/widgets/authentik/component.test.jsx
Normal file
108
src/widgets/authentik/component.test.jsx
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
// @vitest-environment jsdom
|
||||||
|
|
||||||
|
import { screen } from "@testing-library/react";
|
||||||
|
import { afterEach, 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";
|
||||||
|
|
||||||
|
function expectBlockValue(container, label, value) {
|
||||||
|
const blocks = Array.from(container.querySelectorAll(".service-block"));
|
||||||
|
const block = blocks.find((b) => b.textContent?.includes(label));
|
||||||
|
expect(block, `missing block for ${label}`).toBeTruthy();
|
||||||
|
expect(block.textContent).toContain(String(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("widgets/authentik/component", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders placeholders while loading", () => {
|
||||||
|
useWidgetAPI.mockImplementation(() => ({ data: undefined, error: undefined }));
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "authentik", version: 2 } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(3);
|
||||||
|
expect(screen.getByText("authentik.users")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("authentik.loginsLast24H")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("authentik.failedLoginsLast24H")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("computes v2 login/failed counts from action data", () => {
|
||||||
|
useWidgetAPI.mockImplementation((widget, endpoint) => {
|
||||||
|
if (endpoint === "users") return { data: { pagination: { count: 10 } }, error: undefined };
|
||||||
|
if (endpoint === "loginv2")
|
||||||
|
return {
|
||||||
|
data: [
|
||||||
|
{ action: "login", count: 2 },
|
||||||
|
{ action: "logout", count: 9 },
|
||||||
|
],
|
||||||
|
error: undefined,
|
||||||
|
};
|
||||||
|
if (endpoint === "login_failedv2") return { data: [{ count: 3 }, { count: null }], error: undefined };
|
||||||
|
return { data: undefined, error: undefined };
|
||||||
|
});
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "authentik", version: 2 } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(3);
|
||||||
|
expectBlockValue(container, "authentik.users", 10);
|
||||||
|
expectBlockValue(container, "authentik.loginsLast24H", 2);
|
||||||
|
expectBlockValue(container, "authentik.failedLoginsLast24H", 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("computes v1 login/failed counts for entries within the last 24h window", () => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
vi.setSystemTime(new Date("2026-01-02T00:00:00Z"));
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
const oneHourAgo = now - 60 * 60 * 1000;
|
||||||
|
const twentyFiveHoursAgo = now - 25 * 60 * 60 * 1000;
|
||||||
|
|
||||||
|
useWidgetAPI.mockImplementation((widget, endpoint) => {
|
||||||
|
if (endpoint === "users") return { data: { pagination: { count: 5 } }, error: undefined };
|
||||||
|
if (endpoint === "login")
|
||||||
|
return {
|
||||||
|
data: [
|
||||||
|
{ x_cord: oneHourAgo, y_cord: 2 },
|
||||||
|
{ x_cord: twentyFiveHoursAgo, y_cord: 100 },
|
||||||
|
],
|
||||||
|
error: undefined,
|
||||||
|
};
|
||||||
|
if (endpoint === "login_failed")
|
||||||
|
return {
|
||||||
|
data: [
|
||||||
|
{ x_cord: oneHourAgo, y_cord: 1 },
|
||||||
|
{ x_cord: twentyFiveHoursAgo, y_cord: 50 },
|
||||||
|
],
|
||||||
|
error: undefined,
|
||||||
|
};
|
||||||
|
return { data: undefined, error: undefined };
|
||||||
|
});
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "authentik", version: 1 } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(3);
|
||||||
|
expectBlockValue(container, "authentik.users", 5);
|
||||||
|
expectBlockValue(container, "authentik.loginsLast24H", 2);
|
||||||
|
expectBlockValue(container, "authentik.failedLoginsLast24H", 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
60
src/widgets/autobrr/component.test.jsx
Normal file
60
src/widgets/autobrr/component.test.jsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// @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";
|
||||||
|
|
||||||
|
function expectBlockValue(container, label, value) {
|
||||||
|
const blocks = Array.from(container.querySelectorAll(".service-block"));
|
||||||
|
const block = blocks.find((b) => b.textContent?.includes(label));
|
||||||
|
expect(block, `missing block for ${label}`).toBeTruthy();
|
||||||
|
expect(block.textContent).toContain(String(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("widgets/autobrr/component", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders placeholders while loading", () => {
|
||||||
|
useWidgetAPI.mockImplementation(() => ({ data: undefined, error: undefined }));
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "autobrr" } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
expect(screen.getByText("autobrr.approvedPushes")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("autobrr.rejectedPushes")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("autobrr.filters")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("autobrr.indexers")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders values when loaded", () => {
|
||||||
|
useWidgetAPI.mockImplementation((widget, endpoint) => {
|
||||||
|
if (endpoint === "stats") return { data: { push_approved_count: 1, push_rejected_count: 2 }, error: undefined };
|
||||||
|
if (endpoint === "filters") return { data: [{}, {}], error: undefined };
|
||||||
|
if (endpoint === "indexers") return { data: [{}], error: undefined };
|
||||||
|
return { data: undefined, error: undefined };
|
||||||
|
});
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "autobrr" } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
expectBlockValue(container, "autobrr.approvedPushes", 1);
|
||||||
|
expectBlockValue(container, "autobrr.rejectedPushes", 2);
|
||||||
|
expectBlockValue(container, "autobrr.filters", 2);
|
||||||
|
expectBlockValue(container, "autobrr.indexers", 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
101
src/widgets/azuredevops/component.test.jsx
Normal file
101
src/widgets/azuredevops/component.test.jsx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// @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";
|
||||||
|
|
||||||
|
function expectBlockValue(container, label, value) {
|
||||||
|
const blocks = Array.from(container.querySelectorAll(".service-block"));
|
||||||
|
const block = blocks.find((b) => b.textContent?.includes(label));
|
||||||
|
expect(block, `missing block for ${label}`).toBeTruthy();
|
||||||
|
expect(block.textContent).toContain(String(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("widgets/azuredevops/component", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders placeholders while loading", () => {
|
||||||
|
useWidgetAPI.mockImplementation(() => ({ data: undefined, error: undefined }));
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "azuredevops" } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
expect(screen.getByText("azuredevops.result")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("azuredevops.totalPrs")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("azuredevops.myPrs")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("azuredevops.approved")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders pipeline result without PR blocks when includePR is false", () => {
|
||||||
|
useWidgetAPI.mockImplementation((widget, endpoint) => {
|
||||||
|
if (endpoint === null) return { data: undefined, error: undefined };
|
||||||
|
if (endpoint === "pipeline")
|
||||||
|
return { data: { value: [{ result: "succeeded", status: "completed" }] }, error: undefined };
|
||||||
|
return { data: undefined, error: undefined };
|
||||||
|
});
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "azuredevops" } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(1);
|
||||||
|
expectBlockValue(container, "azuredevops.result", "azuredevops.succeeded");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders pipeline status and PR aggregates when includePR is true", () => {
|
||||||
|
useWidgetAPI.mockImplementation((widget, endpoint) => {
|
||||||
|
if (endpoint === "pipeline") return { data: { value: [{ status: "inProgress" }] }, error: undefined };
|
||||||
|
if (endpoint === "pr")
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
count: 3,
|
||||||
|
value: [
|
||||||
|
{ createdBy: { uniqueName: "me@example.com" }, reviewers: [{ vote: 5 }] },
|
||||||
|
{ createdBy: { uniqueName: "me@example.com" }, reviewers: [{ vote: 0 }] },
|
||||||
|
{ createdBy: { uniqueName: "other@example.com" }, reviewers: [{ vote: 10 }] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
error: undefined,
|
||||||
|
};
|
||||||
|
return { data: undefined, error: undefined };
|
||||||
|
});
|
||||||
|
|
||||||
|
const service = {
|
||||||
|
widget: { type: "azuredevops", userEmail: "me@example.com", repositoryId: "repo1" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
expectBlockValue(container, "azuredevops.status", "azuredevops.inProgress");
|
||||||
|
expectBlockValue(container, "azuredevops.totalPrs", 3);
|
||||||
|
expectBlockValue(container, "azuredevops.myPrs", 2);
|
||||||
|
expectBlockValue(container, "azuredevops.approved", 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders PR error message when PR call returns an errorCode", () => {
|
||||||
|
useWidgetAPI.mockImplementation((widget, endpoint) => {
|
||||||
|
if (endpoint === "pipeline") return { data: { value: [{ result: "succeeded" }] }, error: undefined };
|
||||||
|
if (endpoint === "pr") return { data: { errorCode: 1, message: "Bad PR" }, error: undefined };
|
||||||
|
return { data: undefined, error: undefined };
|
||||||
|
});
|
||||||
|
|
||||||
|
const service = { widget: { type: "azuredevops", userEmail: "me@example.com", repositoryId: "repo1" } };
|
||||||
|
renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||||
|
|
||||||
|
expect(screen.getByText("Bad PR")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
81
src/widgets/backrest/component.test.jsx
Normal file
81
src/widgets/backrest/component.test.jsx
Normal file
@@ -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";
|
||||||
|
|
||||||
|
const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() }));
|
||||||
|
|
||||||
|
vi.mock("utils/proxy/use-widget-api", () => ({
|
||||||
|
default: useWidgetAPI,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import Component from "./component";
|
||||||
|
|
||||||
|
describe("widgets/backrest/component", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defaults widget.fields and filters placeholders down to 4 blocks while loading", () => {
|
||||||
|
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||||
|
|
||||||
|
const service = { widget: { type: "backrest" } };
|
||||||
|
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||||
|
|
||||||
|
expect(service.widget.fields).toEqual([
|
||||||
|
"num_success_latest",
|
||||||
|
"num_failure_latest",
|
||||||
|
"num_failure_30",
|
||||||
|
"bytes_added_30",
|
||||||
|
]);
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
|
||||||
|
expect(screen.getByText("backrest.num_success_latest")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("backrest.num_failure_latest")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("backrest.num_failure_30")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("backrest.bytes_added_30")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("backrest.num_plans")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("truncates widget.fields to 4", () => {
|
||||||
|
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||||
|
|
||||||
|
const service = {
|
||||||
|
widget: { type: "backrest", fields: ["a", "b", "c", "d", "e"] },
|
||||||
|
};
|
||||||
|
|
||||||
|
renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||||
|
|
||||||
|
expect(service.widget.fields).toEqual(["a", "b", "c", "d"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders values and respects field filtering", () => {
|
||||||
|
useWidgetAPI.mockReturnValue({
|
||||||
|
data: {
|
||||||
|
numPlans: 10,
|
||||||
|
numSuccessLatest: 1,
|
||||||
|
numFailureLatest: 2,
|
||||||
|
numSuccess30Days: 3,
|
||||||
|
numFailure30Days: 4,
|
||||||
|
bytesAdded30Days: 500,
|
||||||
|
},
|
||||||
|
error: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "backrest" } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Default fields exclude num_plans and num_success_30
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
expect(screen.queryByText("backrest.num_plans")).toBeNull();
|
||||||
|
expect(screen.queryByText("backrest.num_success_30")).toBeNull();
|
||||||
|
|
||||||
|
expect(screen.getByText("1")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("2")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("4")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("500")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
56
src/widgets/bazarr/component.test.jsx
Normal file
56
src/widgets/bazarr/component.test.jsx
Normal file
@@ -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";
|
||||||
|
|
||||||
|
const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() }));
|
||||||
|
|
||||||
|
vi.mock("utils/proxy/use-widget-api", () => ({
|
||||||
|
default: useWidgetAPI,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import Component from "./component";
|
||||||
|
|
||||||
|
describe("widgets/bazarr/component", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders placeholders while loading", () => {
|
||||||
|
useWidgetAPI.mockImplementation(() => ({ data: undefined, error: undefined }));
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "bazarr" } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(2);
|
||||||
|
expect(screen.getByText("bazarr.missingEpisodes")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("bazarr.missingMovies")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders error UI when either endpoint errors", () => {
|
||||||
|
useWidgetAPI
|
||||||
|
.mockReturnValueOnce({ data: undefined, error: { message: "episodes bad" } })
|
||||||
|
.mockReturnValueOnce({ data: undefined, error: undefined });
|
||||||
|
|
||||||
|
renderWithProviders(<Component service={{ widget: { type: "bazarr" } }} />, { settings: { hideErrors: false } });
|
||||||
|
|
||||||
|
expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders counts when loaded", () => {
|
||||||
|
useWidgetAPI
|
||||||
|
.mockReturnValueOnce({ data: { total: 11 }, error: undefined })
|
||||||
|
.mockReturnValueOnce({ data: { total: 22 }, error: undefined });
|
||||||
|
|
||||||
|
const { container } = renderWithProviders(<Component service={{ widget: { type: "bazarr" } }} />, {
|
||||||
|
settings: { hideErrors: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(2);
|
||||||
|
expect(screen.getByText("11")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("22")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -18,6 +18,7 @@ vi.mock("next-i18next", () => ({
|
|||||||
if (key === "common.bbytes") return String(opts?.value ?? "");
|
if (key === "common.bbytes") return String(opts?.value ?? "");
|
||||||
if (key === "common.byterate") return String(opts?.value ?? "");
|
if (key === "common.byterate") return String(opts?.value ?? "");
|
||||||
if (key === "common.duration") return String(opts?.value ?? "");
|
if (key === "common.duration") return String(opts?.value ?? "");
|
||||||
|
if (key === "common.ms") return String(opts?.value ?? "");
|
||||||
return key;
|
return key;
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user