mirror of
https://github.com/gethomepage/homepage.git
synced 2026-09-28 23:11:17 -07:00
Chore: homepage tests (#6278)
This commit is contained in:
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,102 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import createMockRes from "test-utils/create-mock-res";
|
||||
|
||||
const { httpProxy, getServiceWidget, validateWidgetData, logger } = vi.hoisted(() => ({
|
||||
httpProxy: vi.fn(),
|
||||
getServiceWidget: vi.fn(),
|
||||
validateWidgetData: vi.fn(() => true),
|
||||
logger: {
|
||||
debug: vi.fn(),
|
||||
error: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("utils/logger", () => ({
|
||||
default: () => logger,
|
||||
}));
|
||||
|
||||
vi.mock("utils/config/service-helpers", () => ({
|
||||
default: getServiceWidget,
|
||||
}));
|
||||
|
||||
vi.mock("utils/proxy/http", () => ({
|
||||
httpProxy,
|
||||
}));
|
||||
|
||||
vi.mock("utils/proxy/validate-widget-data", () => ({
|
||||
default: validateWidgetData,
|
||||
}));
|
||||
|
||||
vi.mock("widgets/widgets", () => ({
|
||||
default: {
|
||||
jellyfin: {
|
||||
api: "{url}/{endpoint}",
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
import jellyfinProxyHandler from "./proxy";
|
||||
|
||||
describe("widgets/jellyfin/proxy", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
validateWidgetData.mockReturnValue(true);
|
||||
});
|
||||
|
||||
it("adds MediaBrowser auth header and applies an optional mapping function", async () => {
|
||||
getServiceWidget.mockResolvedValue({
|
||||
type: "jellyfin",
|
||||
url: "http://jf",
|
||||
key: "abc",
|
||||
service_group: "mygroup",
|
||||
service_name: "myservice",
|
||||
});
|
||||
|
||||
httpProxy.mockResolvedValueOnce([200, "application/json", { items: [1] }]);
|
||||
|
||||
const req = { method: "GET", query: { group: "g", service: "svc", endpoint: "Users", index: "0" } };
|
||||
const res = createMockRes();
|
||||
|
||||
await jellyfinProxyHandler(req, res, () => ({ mapped: true }));
|
||||
|
||||
expect(httpProxy).toHaveBeenCalledTimes(1);
|
||||
expect(httpProxy.mock.calls[0][0].toString()).toBe("http://jf/Users");
|
||||
expect(httpProxy.mock.calls[0][1].headers.Authorization).toBe(
|
||||
'MediaBrowser Token="abc", Client="Homepage", Device="Homepage", DeviceId="mygroup-myservice", Version="1.0.0"',
|
||||
);
|
||||
expect(validateWidgetData).toHaveBeenCalledWith(expect.objectContaining({ type: "jellyfin" }), "Users", {
|
||||
items: [1],
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body).toEqual({ mapped: true });
|
||||
expect(res.setHeader).toHaveBeenCalledWith("Content-Type", "application/json");
|
||||
});
|
||||
|
||||
it("returns 500 when data validation fails", async () => {
|
||||
validateWidgetData.mockReturnValue(false);
|
||||
getServiceWidget.mockResolvedValue({ type: "jellyfin", url: "http://jf", key: "abc" });
|
||||
httpProxy.mockResolvedValueOnce([200, "application/json", { nope: true }]);
|
||||
|
||||
const req = { method: "GET", query: { group: "g", service: "svc", endpoint: "Users", index: "0" } };
|
||||
const res = createMockRes();
|
||||
|
||||
await jellyfinProxyHandler(req, res);
|
||||
|
||||
expect(res.statusCode).toBe(500);
|
||||
expect(res.body.error.message).toBe("Invalid data");
|
||||
});
|
||||
|
||||
it("ends the response for 204 responses", async () => {
|
||||
getServiceWidget.mockResolvedValue({ type: "jellyfin", url: "http://jf", key: "abc" });
|
||||
httpProxy.mockResolvedValueOnce([204, "application/json", {}]);
|
||||
|
||||
const req = { method: "GET", query: { group: "g", service: "svc", endpoint: "Users", index: "0" } };
|
||||
const res = createMockRes();
|
||||
|
||||
await jellyfinProxyHandler(req, res);
|
||||
|
||||
expect(res.statusCode).toBe(204);
|
||||
expect(res.end).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { describe, it } from "vitest";
|
||||
|
||||
import { expectWidgetConfigShape } from "test-utils/widget-config";
|
||||
|
||||
import widget from "./widget";
|
||||
|
||||
describe("jellyfin widget config", () => {
|
||||
it("exports a valid widget config", () => {
|
||||
expectWidgetConfigShape(widget);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user