mirror of
https://github.com/gethomepage/homepage.git
synced 2026-02-07 16:30:52 +08:00
Test: 10 more widget components (F)
This commit is contained in:
78
src/widgets/homebox/component.test.jsx
Normal file
78
src/widgets/homebox/component.test.jsx
Normal file
@@ -0,0 +1,78 @@
|
||||
// @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, { homeboxDefaultFields } 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/homebox/component", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("defaults fields and filters to 3 blocks while loading", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||
|
||||
const service = { widget: { type: "homebox", url: "http://x" } };
|
||||
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||
|
||||
expect(service.widget.fields).toEqual(homeboxDefaultFields);
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(3);
|
||||
expect(screen.getByText("homebox.items")).toBeInTheDocument();
|
||||
expect(screen.getByText("homebox.locations")).toBeInTheDocument();
|
||||
expect(screen.getByText("homebox.totalValue")).toBeInTheDocument();
|
||||
expect(screen.queryByText("homebox.labels")).toBeNull();
|
||||
expect(screen.queryByText("homebox.users")).toBeNull();
|
||||
expect(screen.queryByText("homebox.totalWithWarranty")).toBeNull();
|
||||
});
|
||||
|
||||
it("renders error UI when widget API errors", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: { message: "nope" } });
|
||||
|
||||
renderWithProviders(<Component service={{ widget: { type: "homebox", url: "http://x" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0);
|
||||
expect(screen.getByText("nope")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders values when loaded (currency formatting delegated to i18n)", () => {
|
||||
useWidgetAPI.mockReturnValue({
|
||||
data: {
|
||||
items: 10,
|
||||
totalWithWarranty: 2,
|
||||
locations: 3,
|
||||
labels: 4,
|
||||
users: 5,
|
||||
totalValue: 123.45,
|
||||
currencyCode: "USD",
|
||||
},
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
const { container } = renderWithProviders(
|
||||
<Component service={{ widget: { type: "homebox", url: "http://x" } }} />,
|
||||
{
|
||||
settings: { hideErrors: false },
|
||||
},
|
||||
);
|
||||
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(3);
|
||||
expectBlockValue(container, "homebox.items", 10);
|
||||
expectBlockValue(container, "homebox.locations", 3);
|
||||
expectBlockValue(container, "homebox.totalValue", 123.45);
|
||||
});
|
||||
});
|
||||
63
src/widgets/homebridge/component.test.jsx
Normal file
63
src/widgets/homebridge/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/homebridge/component", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders placeholders while loading", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||
|
||||
const { container } = renderWithProviders(
|
||||
<Component service={{ widget: { type: "homebridge", url: "http://x" } }} />,
|
||||
{ settings: { hideErrors: false } },
|
||||
);
|
||||
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(3);
|
||||
expect(screen.getByText("widget.status")).toBeInTheDocument();
|
||||
expect(screen.getByText("homebridge.updates")).toBeInTheDocument();
|
||||
expect(screen.getByText("homebridge.child_bridges")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders error UI when widget API errors", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: { message: "nope" } });
|
||||
|
||||
renderWithProviders(<Component service={{ widget: { type: "homebridge", url: "http://x" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0);
|
||||
expect(screen.getByText("nope")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders update status and child bridge summary when child bridges exist", () => {
|
||||
useWidgetAPI.mockReturnValue({
|
||||
data: {
|
||||
status: "ok",
|
||||
updateAvailable: true,
|
||||
plugins: { updatesAvailable: 0 },
|
||||
childBridges: { total: 2, running: 1 },
|
||||
},
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
renderWithProviders(<Component service={{ widget: { type: "homebridge", url: "http://x" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(screen.getByText("homebridge.ok")).toBeInTheDocument();
|
||||
expect(screen.getByText("homebridge.update_available")).toBeInTheDocument();
|
||||
// key is returned by the i18n mock; presence indicates the conditional block is rendered.
|
||||
expect(screen.getByText("homebridge.child_bridges_status")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
31
src/widgets/iframe/component.test.jsx
Normal file
31
src/widgets/iframe/component.test.jsx
Normal file
@@ -0,0 +1,31 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { renderWithProviders } from "test-utils/render-with-providers";
|
||||
|
||||
import Component from "./component";
|
||||
|
||||
describe("widgets/iframe/component", () => {
|
||||
it("renders an iframe with the configured src/title and classes", () => {
|
||||
const service = {
|
||||
widget: {
|
||||
type: "iframe",
|
||||
name: "My Frame",
|
||||
src: "http://example.test",
|
||||
classes: "h-10 w-10",
|
||||
allowScrolling: "no",
|
||||
},
|
||||
};
|
||||
|
||||
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||
|
||||
const iframe = container.querySelector("iframe");
|
||||
expect(iframe).toBeTruthy();
|
||||
expect(iframe.getAttribute("src")).toBe("http://example.test");
|
||||
expect(iframe.getAttribute("title")).toBe("My Frame");
|
||||
expect(iframe.getAttribute("name")).toBe("My Frame");
|
||||
expect(iframe.getAttribute("scrolling")).toBe("no");
|
||||
expect(iframe.className).toContain("h-10 w-10");
|
||||
});
|
||||
});
|
||||
73
src/widgets/immich/component.test.jsx
Normal file
73
src/widgets/immich/component.test.jsx
Normal file
@@ -0,0 +1,73 @@
|
||||
// @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/immich/component", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("uses v1 endpoints and renders placeholders while loading", () => {
|
||||
useWidgetAPI
|
||||
.mockReturnValueOnce({ data: undefined, error: undefined }) // version
|
||||
.mockReturnValueOnce({ data: undefined, error: undefined }); // stats
|
||||
|
||||
const { container } = renderWithProviders(<Component service={{ widget: { type: "immich", url: "http://x" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(useWidgetAPI.mock.calls[0][1]).toBe("version");
|
||||
expect(useWidgetAPI.mock.calls[1][1]).toBe("stats");
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||
expect(screen.getByText("immich.users")).toBeInTheDocument();
|
||||
expect(screen.getByText("immich.photos")).toBeInTheDocument();
|
||||
expect(screen.getByText("immich.videos")).toBeInTheDocument();
|
||||
expect(screen.getByText("immich.storage")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("selects the v1 statistics endpoint when version is > 1.84", () => {
|
||||
useWidgetAPI.mockReturnValueOnce({ data: { major: 1, minor: 85 }, error: undefined }).mockReturnValueOnce({
|
||||
data: { usageByUser: [{ id: 1 }, { id: 2 }], photos: 3, videos: 4, usage: "9 GiB" },
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
const { container } = renderWithProviders(<Component service={{ widget: { type: "immich", url: "http://x" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(useWidgetAPI.mock.calls[1][1]).toBe("statistics");
|
||||
expectBlockValue(container, "immich.users", 2);
|
||||
expectBlockValue(container, "immich.photos", 3);
|
||||
expectBlockValue(container, "immich.videos", 4);
|
||||
expectBlockValue(container, "immich.storage", "9 GiB");
|
||||
});
|
||||
|
||||
it("uses v2 endpoints when widget.version === 2", () => {
|
||||
useWidgetAPI.mockReturnValueOnce({ data: { major: 2, minor: 0 }, error: undefined }).mockReturnValueOnce({
|
||||
data: { usageByUser: [], photos: 0, videos: 0, usage: 0 },
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
renderWithProviders(<Component service={{ widget: { type: "immich", url: "http://x", version: 2 } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(useWidgetAPI.mock.calls[0][1]).toBe("version_v2");
|
||||
expect(useWidgetAPI.mock.calls[1][1]).toBe("statistics_v2");
|
||||
});
|
||||
});
|
||||
71
src/widgets/jackett/component.test.jsx
Normal file
71
src/widgets/jackett/component.test.jsx
Normal file
@@ -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/jackett/component", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders placeholders while loading", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||
|
||||
const { container } = renderWithProviders(
|
||||
<Component service={{ widget: { type: "jackett", url: "http://x" } }} />,
|
||||
{
|
||||
settings: { hideErrors: false },
|
||||
},
|
||||
);
|
||||
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(2);
|
||||
expect(screen.getByText("jackett.configured")).toBeInTheDocument();
|
||||
expect(screen.getByText("jackett.errored")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders error UI when widget API errors", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: { message: "nope" } });
|
||||
|
||||
renderWithProviders(<Component service={{ widget: { type: "jackett", url: "http://x" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0);
|
||||
expect(screen.getByText("nope")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders configured and errored counts when loaded", () => {
|
||||
useWidgetAPI.mockReturnValue({
|
||||
data: [
|
||||
{ id: 1, last_error: "" },
|
||||
{ id: 2, last_error: "boom" },
|
||||
{ id: 3, last_error: null },
|
||||
],
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
const { container } = renderWithProviders(
|
||||
<Component service={{ widget: { type: "jackett", url: "http://x" } }} />,
|
||||
{
|
||||
settings: { hideErrors: false },
|
||||
},
|
||||
);
|
||||
|
||||
expectBlockValue(container, "jackett.configured", 3);
|
||||
expectBlockValue(container, "jackett.errored", 1);
|
||||
});
|
||||
});
|
||||
83
src/widgets/jdownloader/component.test.jsx
Normal file
83
src/widgets/jdownloader/component.test.jsx
Normal file
@@ -0,0 +1,83 @@
|
||||
// @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/jdownloader/component", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders placeholders while loading", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||
|
||||
const { container } = renderWithProviders(
|
||||
<Component service={{ widget: { type: "jdownloader", url: "http://x" } }} />,
|
||||
{ settings: { hideErrors: false } },
|
||||
);
|
||||
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||
expect(screen.getByText("jdownloader.downloadCount")).toBeInTheDocument();
|
||||
expect(screen.getByText("jdownloader.downloadTotalBytes")).toBeInTheDocument();
|
||||
expect(screen.getByText("jdownloader.downloadBytesRemaining")).toBeInTheDocument();
|
||||
expect(screen.getByText("jdownloader.downloadSpeed")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls the unified endpoint with a 30s refresh interval", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||
|
||||
renderWithProviders(<Component service={{ widget: { type: "jdownloader", url: "http://x" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(useWidgetAPI.mock.calls[0][1]).toBe("unified");
|
||||
expect(useWidgetAPI.mock.calls[0][2]?.refreshInterval).toBe(30000);
|
||||
});
|
||||
|
||||
it("renders error UI when widget API errors", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: { message: "nope" } });
|
||||
|
||||
renderWithProviders(<Component service={{ widget: { type: "jdownloader", url: "http://x" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0);
|
||||
expect(screen.getByText("nope")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders values when loaded", () => {
|
||||
useWidgetAPI.mockReturnValue({
|
||||
data: {
|
||||
downloadCount: 1,
|
||||
totalBytes: 100,
|
||||
bytesRemaining: 40,
|
||||
totalSpeed: 10,
|
||||
},
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
const { container } = renderWithProviders(
|
||||
<Component service={{ widget: { type: "jdownloader", url: "http://x" } }} />,
|
||||
{ settings: { hideErrors: false } },
|
||||
);
|
||||
|
||||
expectBlockValue(container, "jdownloader.downloadCount", 1);
|
||||
expectBlockValue(container, "jdownloader.downloadTotalBytes", 100);
|
||||
expectBlockValue(container, "jdownloader.downloadBytesRemaining", 40);
|
||||
expectBlockValue(container, "jdownloader.downloadSpeed", 10);
|
||||
});
|
||||
});
|
||||
92
src/widgets/jellyfin/component.test.jsx
Normal file
92
src/widgets/jellyfin/component.test.jsx
Normal file
@@ -0,0 +1,92 @@
|
||||
// @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/jellyfin/component", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders CountBlocks placeholders while loading when enableBlocks is true", () => {
|
||||
useWidgetAPI
|
||||
.mockReturnValueOnce({ data: undefined, error: undefined, mutate: vi.fn() }) // sessions
|
||||
.mockReturnValueOnce({ data: undefined, error: undefined }); // count
|
||||
|
||||
renderWithProviders(
|
||||
<Component
|
||||
service={{
|
||||
widget: { type: "jellyfin", url: "http://x", enableBlocks: true },
|
||||
}}
|
||||
/>,
|
||||
{ settings: { hideErrors: false } },
|
||||
);
|
||||
|
||||
expect(screen.getByText("jellyfin.movies")).toBeInTheDocument();
|
||||
expect(screen.getByText("jellyfin.series")).toBeInTheDocument();
|
||||
expect(screen.getByText("jellyfin.episodes")).toBeInTheDocument();
|
||||
expect(screen.getByText("jellyfin.songs")).toBeInTheDocument();
|
||||
expect(screen.getAllByText("-").length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("renders the no-active message when there are no playing sessions", () => {
|
||||
useWidgetAPI
|
||||
.mockReturnValueOnce({ data: [], error: undefined, mutate: vi.fn() }) // sessions
|
||||
.mockReturnValueOnce({
|
||||
data: { MovieCount: 1, SeriesCount: 2, EpisodeCount: 3, SongCount: 4 },
|
||||
error: undefined,
|
||||
}); // count
|
||||
|
||||
renderWithProviders(
|
||||
<Component
|
||||
service={{
|
||||
widget: { type: "jellyfin", url: "http://x", enableBlocks: true },
|
||||
}}
|
||||
/>,
|
||||
{ settings: { hideErrors: false } },
|
||||
);
|
||||
|
||||
expect(screen.getByText("jellyfin.no_active")).toBeInTheDocument();
|
||||
expect(screen.getByText("1")).toBeInTheDocument();
|
||||
expect(screen.getByText("2")).toBeInTheDocument();
|
||||
expect(screen.getByText("3")).toBeInTheDocument();
|
||||
expect(screen.getByText("4")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders a single now-playing entry (expanded to two rows by default)", () => {
|
||||
useWidgetAPI
|
||||
.mockReturnValueOnce({
|
||||
data: [
|
||||
{
|
||||
Id: "s1",
|
||||
UserName: "u1",
|
||||
NowPlayingItem: { Name: "Movie1", Type: "Movie", RunTimeTicks: 600000000 },
|
||||
PlayState: { PositionTicks: 0, IsPaused: false, IsMuted: false },
|
||||
TranscodingInfo: { IsVideoDirect: true },
|
||||
},
|
||||
],
|
||||
error: undefined,
|
||||
mutate: vi.fn(),
|
||||
})
|
||||
.mockReturnValueOnce({
|
||||
data: { MovieCount: 0, SeriesCount: 0, EpisodeCount: 0, SongCount: 0 },
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
renderWithProviders(<Component service={{ widget: { type: "jellyfin", url: "http://x" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(screen.getByText("Movie1")).toBeInTheDocument();
|
||||
// Time strings are rendered in a combined node (e.g. "00:00/01:00").
|
||||
expect(screen.getByText(/00:00/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/01:00/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
63
src/widgets/jellyseerr/component.test.jsx
Normal file
63
src/widgets/jellyseerr/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, { jellyseerrDefaultFields } from "./component";
|
||||
|
||||
describe("widgets/jellyseerr/component", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("defaults fields and filters to 3 blocks while loading when issues are not enabled", () => {
|
||||
useWidgetAPI
|
||||
.mockReturnValueOnce({ data: undefined, error: undefined }) // request/count
|
||||
.mockReturnValueOnce({ data: undefined, error: undefined }); // issue/count disabled (endpoint = "")
|
||||
|
||||
const service = { widget: { type: "jellyseerr", url: "http://x" } };
|
||||
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||
|
||||
expect(service.widget.fields).toEqual(jellyseerrDefaultFields);
|
||||
expect(useWidgetAPI.mock.calls[1][1]).toBe("");
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(3);
|
||||
expect(screen.getByText("jellyseerr.pending")).toBeInTheDocument();
|
||||
expect(screen.getByText("jellyseerr.approved")).toBeInTheDocument();
|
||||
expect(screen.getByText("jellyseerr.available")).toBeInTheDocument();
|
||||
expect(screen.queryByText("jellyseerr.issues")).toBeNull();
|
||||
});
|
||||
|
||||
it("renders issues when enabled (and calls the issue/count endpoint)", () => {
|
||||
useWidgetAPI
|
||||
.mockReturnValueOnce({ data: { pending: 1, approved: 2, available: 3 }, error: undefined })
|
||||
.mockReturnValueOnce({ data: { open: 1, total: 2 }, error: undefined });
|
||||
|
||||
const service = {
|
||||
widget: { type: "jellyseerr", url: "http://x", fields: ["pending", "approved", "available", "issues"] },
|
||||
};
|
||||
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||
|
||||
expect(useWidgetAPI.mock.calls[1][1]).toBe("issue/count");
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||
expect(screen.getByText("1 / 2")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders error UI when issues are enabled and issue/count errors", () => {
|
||||
useWidgetAPI
|
||||
.mockReturnValueOnce({ data: { pending: 0, approved: 0, available: 0 }, error: undefined })
|
||||
.mockReturnValueOnce({ data: undefined, error: { message: "nope" } });
|
||||
|
||||
renderWithProviders(
|
||||
<Component service={{ widget: { type: "jellyseerr", url: "http://x", fields: ["issues"] } }} />,
|
||||
{ settings: { hideErrors: false } },
|
||||
);
|
||||
|
||||
expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0);
|
||||
expect(screen.getByText("nope")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
79
src/widgets/karakeep/component.test.jsx
Normal file
79
src/widgets/karakeep/component.test.jsx
Normal file
@@ -0,0 +1,79 @@
|
||||
// @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, { karakeepDefaultFields } 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/karakeep/component", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("defaults fields and filters to 4 blocks while loading", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||
|
||||
const service = { widget: { type: "karakeep", url: "http://x" } };
|
||||
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||
|
||||
expect(service.widget.fields).toEqual(karakeepDefaultFields);
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||
expect(screen.getByText("karakeep.bookmarks")).toBeInTheDocument();
|
||||
expect(screen.getByText("karakeep.favorites")).toBeInTheDocument();
|
||||
expect(screen.getByText("karakeep.archived")).toBeInTheDocument();
|
||||
expect(screen.getByText("karakeep.highlights")).toBeInTheDocument();
|
||||
expect(screen.queryByText("karakeep.lists")).toBeNull();
|
||||
expect(screen.queryByText("karakeep.tags")).toBeNull();
|
||||
});
|
||||
|
||||
it("caps widget.fields at 4 entries", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||
|
||||
const service = { widget: { type: "karakeep", fields: ["tags", "lists", "bookmarks", "favorites", "archived"] } };
|
||||
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||
|
||||
expect(service.widget.fields).toEqual(["tags", "lists", "bookmarks", "favorites"]);
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||
expect(screen.getByText("karakeep.tags")).toBeInTheDocument();
|
||||
expect(screen.getByText("karakeep.lists")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders values when loaded", () => {
|
||||
useWidgetAPI.mockReturnValue({
|
||||
data: {
|
||||
numBookmarks: 1,
|
||||
numFavorites: 2,
|
||||
numArchived: 3,
|
||||
numHighlights: 4,
|
||||
numLists: 5,
|
||||
numTags: 6,
|
||||
},
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
const { container } = renderWithProviders(
|
||||
<Component service={{ widget: { type: "karakeep", url: "http://x" } }} />,
|
||||
{
|
||||
settings: { hideErrors: false },
|
||||
},
|
||||
);
|
||||
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||
expectBlockValue(container, "karakeep.bookmarks", 1);
|
||||
expectBlockValue(container, "karakeep.favorites", 2);
|
||||
expectBlockValue(container, "karakeep.archived", 3);
|
||||
expectBlockValue(container, "karakeep.highlights", 4);
|
||||
});
|
||||
});
|
||||
51
src/widgets/kavita/component.test.jsx
Normal file
51
src/widgets/kavita/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/kavita/component", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders placeholders while loading", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||
|
||||
const { container } = renderWithProviders(<Component service={{ widget: { type: "kavita", url: "http://x" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(2);
|
||||
expect(screen.getByText("kavita.seriesCount")).toBeInTheDocument();
|
||||
expect(screen.getByText("kavita.totalFiles")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders error UI when widget API errors", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: { message: "nope" } });
|
||||
|
||||
renderWithProviders(<Component service={{ widget: { type: "kavita", url: "http://x" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0);
|
||||
expect(screen.getByText("nope")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders counts when loaded", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: { seriesCount: 12, totalFiles: 34 }, error: undefined });
|
||||
|
||||
renderWithProviders(<Component service={{ widget: { type: "kavita", url: "http://x" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(screen.getByText("12")).toBeInTheDocument();
|
||||
expect(screen.getByText("34")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user