mirror of
https://github.com/gethomepage/homepage.git
synced 2026-09-28 15:01:17 -07:00
Chore: homepage tests (#6278)
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { DateTime } from "luxon";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { EventStub, compareDateTimezoneStub } = vi.hoisted(() => ({
|
||||
EventStub: vi.fn(({ event, showDate, showTime }) => (
|
||||
<div data-testid="event" data-showdate={showDate ? "1" : "0"} data-showtime={showTime ? "1" : "0"}>
|
||||
{event.title}
|
||||
</div>
|
||||
)),
|
||||
compareDateTimezoneStub: vi.fn(
|
||||
(date, event) => date.startOf("day").toISODate() === event.date.startOf("day").toISODate(),
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("./event", () => ({
|
||||
default: EventStub,
|
||||
compareDateTimezone: compareDateTimezoneStub,
|
||||
}));
|
||||
|
||||
import Agenda from "./agenda";
|
||||
|
||||
describe("widgets/calendar/agenda", () => {
|
||||
it("renders an empty placeholder when showDate is not set", () => {
|
||||
const { container } = render(<Agenda service={{ widget: {} }} colorVariants={{}} events={{}} showDate={null} />);
|
||||
expect(container.textContent).toBe("");
|
||||
});
|
||||
|
||||
it("renders a no-events placeholder when there are no events in range", () => {
|
||||
render(<Agenda service={{ widget: {} }} colorVariants={{}} events={{}} showDate={DateTime.now()} />);
|
||||
expect(screen.getByText("calendar.noEventsToday")).toBeInTheDocument();
|
||||
expect(EventStub).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("filters by previousDays, sorts, and enforces maxEvents", () => {
|
||||
const showDate = DateTime.local(2099, 1, 2).startOf("day");
|
||||
const service = { widget: { previousDays: 0, maxEvents: 2, showTime: true } };
|
||||
|
||||
const events = {
|
||||
old: { title: "Old", date: DateTime.local(2099, 1, 1, 0, 0), color: "gray" },
|
||||
a: { title: "A", date: DateTime.local(2099, 1, 2, 10, 0), color: "gray" },
|
||||
b: { title: "B", date: DateTime.local(2099, 1, 3, 10, 0), color: "gray" },
|
||||
c: { title: "C", date: DateTime.local(2099, 1, 4, 10, 0), color: "gray" },
|
||||
};
|
||||
|
||||
render(<Agenda service={service} colorVariants={{}} events={events} showDate={showDate} />);
|
||||
|
||||
// Old is filtered out, C is sliced out by maxEvents.
|
||||
expect(screen.queryByText("Old")).toBeNull();
|
||||
expect(screen.getByText("A")).toBeInTheDocument();
|
||||
expect(screen.getByText("B")).toBeInTheDocument();
|
||||
expect(screen.queryByText("C")).toBeNull();
|
||||
|
||||
const renderedEvents = screen.getAllByTestId("event");
|
||||
expect(renderedEvents).toHaveLength(2);
|
||||
|
||||
// showTime is only true for the selected day.
|
||||
const [first, second] = renderedEvents;
|
||||
expect(first).toHaveAttribute("data-showtime", "1");
|
||||
expect(second).toHaveAttribute("data-showtime", "0");
|
||||
});
|
||||
});
|
||||
@@ -72,7 +72,8 @@ export default function Component({ service }) {
|
||||
widget.integrations
|
||||
?.filter((integration) => integration?.type)
|
||||
.map((integration) => ({
|
||||
service: dynamic(() => import(`./integrations/${integration.type}`)),
|
||||
// Include the extension so Vite/Vitest can statically validate the import base.
|
||||
service: dynamic(() => import(`./integrations/${integration.type}.jsx`)),
|
||||
widget: { ...widget, ...integration },
|
||||
})) ?? [],
|
||||
[widget],
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { screen, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { renderWithProviders } from "test-utils/render-with-providers";
|
||||
|
||||
vi.mock("next/dynamic", () => ({
|
||||
default: () => (props) => (
|
||||
<div
|
||||
data-testid="calendar-integration"
|
||||
data-type={props.config.type}
|
||||
data-start={props.params.start}
|
||||
data-end={props.params.end}
|
||||
data-timezone={props.timezone || ""}
|
||||
/>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("./monthly", () => ({
|
||||
default: ({ showDate }) => <div data-testid="calendar-monthly" data-show={showDate?.toISODate?.() || ""} />,
|
||||
}));
|
||||
|
||||
vi.mock("./agenda", () => ({
|
||||
default: ({ showDate }) => <div data-testid="calendar-agenda" data-show={showDate?.toISODate?.() || ""} />,
|
||||
}));
|
||||
|
||||
import Component from "./component";
|
||||
|
||||
describe("widgets/calendar/component", () => {
|
||||
it("renders monthly view by default", async () => {
|
||||
renderWithProviders(<Component service={{ widget: { type: "calendar", integrations: [] } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(screen.getByTestId("calendar-monthly")).toBeInTheDocument();
|
||||
expect(screen.queryByTestId("calendar-agenda")).toBeNull();
|
||||
|
||||
// showDate is set asynchronously in an effect; ensure it eventually resolves to a date string.
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("calendar-monthly").getAttribute("data-show")).not.toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
it("renders agenda view when configured", async () => {
|
||||
renderWithProviders(<Component service={{ widget: { type: "calendar", view: "agenda", integrations: [] } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(screen.getByTestId("calendar-agenda")).toBeInTheDocument();
|
||||
expect(screen.queryByTestId("calendar-monthly")).toBeNull();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("calendar-agenda").getAttribute("data-show")).not.toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
it("loads configured integrations and passes calculated params", async () => {
|
||||
renderWithProviders(
|
||||
<Component
|
||||
service={{
|
||||
widget: {
|
||||
type: "calendar",
|
||||
timezone: "UTC",
|
||||
integrations: [
|
||||
{
|
||||
type: "sonarr",
|
||||
name: "Sonarr",
|
||||
service_group: "Media",
|
||||
service_name: "Sonarr",
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
/>,
|
||||
{ settings: { hideErrors: false } },
|
||||
);
|
||||
|
||||
const integration = screen.getByTestId("calendar-integration");
|
||||
expect(integration.getAttribute("data-type")).toBe("sonarr");
|
||||
expect(integration.getAttribute("data-timezone")).toBe("UTC");
|
||||
|
||||
await waitFor(() => {
|
||||
// start/end should be yyyy-MM-dd after showDate is set.
|
||||
expect(integration.getAttribute("data-start")).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
||||
expect(integration.getAttribute("data-end")).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { DateTime } from "luxon";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import Event, { compareDateTimezone } from "./event";
|
||||
|
||||
describe("widgets/calendar/event", () => {
|
||||
it("renders an anchor when a url is provided and toggles additional text on hover", () => {
|
||||
const date = DateTime.fromISO("2099-01-01T13:00:00.000Z").setZone("utc");
|
||||
|
||||
render(
|
||||
<Event
|
||||
event={{
|
||||
title: "Primary",
|
||||
additional: "More info",
|
||||
date,
|
||||
color: "gray",
|
||||
url: "https://example.com",
|
||||
isCompleted: true,
|
||||
}}
|
||||
colorVariants={{ gray: "bg-gray-500" }}
|
||||
showDate
|
||||
showTime
|
||||
/>,
|
||||
);
|
||||
|
||||
const link = screen.getByRole("link", { name: /primary/i });
|
||||
expect(link).toHaveAttribute("href", "https://example.com");
|
||||
expect(link).toHaveAttribute("target", "_blank");
|
||||
expect(link).toHaveAttribute("rel", "noopener noreferrer");
|
||||
|
||||
// time is rendered when showTime=true
|
||||
expect(link.textContent).toContain("13:00");
|
||||
|
||||
// default shows title, hover shows `additional`
|
||||
expect(screen.getByText("Primary")).toBeInTheDocument();
|
||||
expect(screen.queryByText("More info")).toBeNull();
|
||||
|
||||
fireEvent.mouseEnter(link);
|
||||
expect(screen.getByText("More info")).toBeInTheDocument();
|
||||
|
||||
fireEvent.mouseLeave(link);
|
||||
expect(screen.getByText("Primary")).toBeInTheDocument();
|
||||
|
||||
// completed icon from react-icons renders an SVG
|
||||
expect(link.querySelector("svg")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("compareDateTimezone matches dates by day", () => {
|
||||
const day = DateTime.fromISO("2099-01-01T00:00:00.000Z").setZone("utc");
|
||||
expect(compareDateTimezone(day, { date: DateTime.fromISO("2099-01-01T23:59:00.000Z").setZone("utc") })).toBe(true);
|
||||
expect(compareDateTimezone(day, { date: DateTime.fromISO("2099-01-02T00:00:00.000Z").setZone("utc") })).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { render, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { useWidgetAPI } = vi.hoisted(() => ({
|
||||
useWidgetAPI: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI }));
|
||||
|
||||
import Integration from "./ical";
|
||||
|
||||
describe("widgets/calendar/integrations/ical", () => {
|
||||
it("adds parsed events within the date range", async () => {
|
||||
useWidgetAPI.mockReturnValue({
|
||||
data: {
|
||||
data: [
|
||||
"BEGIN:VCALENDAR",
|
||||
"VERSION:2.0",
|
||||
"PRODID:-//Test//EN",
|
||||
"BEGIN:VEVENT",
|
||||
"UID:uid1",
|
||||
"DTSTAMP:20990101T000000Z",
|
||||
"DTSTART:20990101T130000Z",
|
||||
"DTEND:20990101T140000Z",
|
||||
"SUMMARY:Test Event",
|
||||
"LOCATION:Office",
|
||||
"URL:https://example.com",
|
||||
"END:VEVENT",
|
||||
"END:VCALENDAR",
|
||||
"",
|
||||
].join("\n"),
|
||||
},
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
const setEvents = vi.fn();
|
||||
render(
|
||||
<Integration
|
||||
config={{ name: "Work", type: "ical", color: "blue", params: { showName: true } }}
|
||||
params={{ start: "2099-01-01T00:00:00.000Z", end: "2099-01-02T00:00:00.000Z" }}
|
||||
setEvents={setEvents}
|
||||
hideErrors
|
||||
timezone="utc"
|
||||
/>,
|
||||
);
|
||||
|
||||
await waitFor(() => expect(setEvents).toHaveBeenCalled());
|
||||
|
||||
const updater = setEvents.mock.calls[0][0];
|
||||
const next = updater({});
|
||||
const entries = Object.values(next);
|
||||
expect(entries).toHaveLength(1);
|
||||
|
||||
const [event] = entries;
|
||||
expect(event.title).toBe("Work: Test Event");
|
||||
expect(event.color).toBe("blue");
|
||||
expect(event.type).toBe("ical");
|
||||
expect(event.additional).toBe("Office");
|
||||
expect(event.url).toBe("https://example.com");
|
||||
expect(event.isCompleted).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { render, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { useWidgetAPI } = vi.hoisted(() => ({
|
||||
useWidgetAPI: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI }));
|
||||
|
||||
import Integration from "./lidarr";
|
||||
|
||||
describe("widgets/calendar/integrations/lidarr", () => {
|
||||
it("adds release events", async () => {
|
||||
useWidgetAPI.mockReturnValue({
|
||||
data: [
|
||||
{ artist: { artistName: "Artist" }, title: "Album", releaseDate: "2099-01-01T00:00:00.000Z", grabbed: true },
|
||||
],
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
const setEvents = vi.fn();
|
||||
render(
|
||||
<Integration
|
||||
config={{ type: "lidarr", color: "green" }}
|
||||
params={{ start: "2099-01-01T00:00:00.000Z", end: "2099-01-02T00:00:00.000Z" }}
|
||||
setEvents={setEvents}
|
||||
hideErrors
|
||||
/>,
|
||||
);
|
||||
|
||||
await waitFor(() => expect(setEvents).toHaveBeenCalled());
|
||||
|
||||
const next = setEvents.mock.calls[0][0]({});
|
||||
expect(Object.keys(next)).toEqual(["Artist - Album"]);
|
||||
expect(next["Artist - Album"].isCompleted).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { render, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { useWidgetAPI } = vi.hoisted(() => ({
|
||||
useWidgetAPI: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI }));
|
||||
|
||||
import Integration from "./radarr";
|
||||
|
||||
describe("widgets/calendar/integrations/radarr", () => {
|
||||
it("adds cinema/physical/digital events", async () => {
|
||||
useWidgetAPI.mockReturnValue({
|
||||
data: [
|
||||
{
|
||||
title: "Movie",
|
||||
titleSlug: "movie",
|
||||
hasFile: false,
|
||||
inCinemas: "2099-01-01T00:00:00.000Z",
|
||||
physicalRelease: "2099-01-02T00:00:00.000Z",
|
||||
digitalRelease: "2099-01-03T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
const setEvents = vi.fn();
|
||||
render(
|
||||
<Integration
|
||||
config={{ type: "radarr", baseUrl: "https://radarr.example", color: "amber" }}
|
||||
params={{ start: "2099-01-01T00:00:00.000Z", end: "2099-01-10T00:00:00.000Z" }}
|
||||
setEvents={setEvents}
|
||||
hideErrors
|
||||
/>,
|
||||
);
|
||||
|
||||
await waitFor(() => expect(setEvents).toHaveBeenCalled());
|
||||
|
||||
const next = setEvents.mock.calls[0][0]({});
|
||||
const keys = Object.keys(next);
|
||||
expect(keys.some((k) => k.includes("calendar.inCinemas"))).toBe(true);
|
||||
expect(keys.some((k) => k.includes("calendar.physicalRelease"))).toBe(true);
|
||||
expect(keys.some((k) => k.includes("calendar.digitalRelease"))).toBe(true);
|
||||
expect(Object.values(next)[0].url).toBe("https://radarr.example/movie/movie");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { render, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { useWidgetAPI } = vi.hoisted(() => ({
|
||||
useWidgetAPI: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI }));
|
||||
|
||||
import Integration from "./readarr";
|
||||
|
||||
describe("widgets/calendar/integrations/readarr", () => {
|
||||
it("adds release events with author name", async () => {
|
||||
useWidgetAPI.mockReturnValue({
|
||||
data: [
|
||||
{
|
||||
title: "Book",
|
||||
seriesTitle: "Series",
|
||||
releaseDate: "2099-01-01T00:00:00.000Z",
|
||||
grabbed: false,
|
||||
author: { authorName: "Author" },
|
||||
authorTitle: "Author Book",
|
||||
},
|
||||
],
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
const setEvents = vi.fn();
|
||||
render(
|
||||
<Integration
|
||||
config={{ type: "readarr", color: "rose" }}
|
||||
params={{ start: "2099-01-01T00:00:00.000Z", end: "2099-01-02T00:00:00.000Z" }}
|
||||
setEvents={setEvents}
|
||||
hideErrors
|
||||
/>,
|
||||
);
|
||||
|
||||
await waitFor(() => expect(setEvents).toHaveBeenCalled());
|
||||
|
||||
const next = setEvents.mock.calls[0][0]({});
|
||||
const [key] = Object.keys(next);
|
||||
expect(key).toContain("Author");
|
||||
expect(key).toContain("Book");
|
||||
expect(key).toContain("(Series)");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { render, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { useWidgetAPI } = vi.hoisted(() => ({
|
||||
useWidgetAPI: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI }));
|
||||
|
||||
import Integration from "./sonarr";
|
||||
|
||||
describe("widgets/calendar/integrations/sonarr", () => {
|
||||
it("adds episode events", async () => {
|
||||
useWidgetAPI.mockReturnValue({
|
||||
data: [
|
||||
{
|
||||
series: { title: "Show", titleSlug: "show" },
|
||||
seasonNumber: 1,
|
||||
episodeNumber: 2,
|
||||
airDateUtc: "2099-01-01T00:00:00.000Z",
|
||||
hasFile: true,
|
||||
},
|
||||
],
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
const setEvents = vi.fn();
|
||||
render(
|
||||
<Integration
|
||||
config={{ type: "sonarr", baseUrl: "https://sonarr.example", color: "teal" }}
|
||||
params={{ start: "2099-01-01T00:00:00.000Z", end: "2099-01-02T00:00:00.000Z" }}
|
||||
setEvents={setEvents}
|
||||
hideErrors
|
||||
/>,
|
||||
);
|
||||
|
||||
await waitFor(() => expect(setEvents).toHaveBeenCalled());
|
||||
|
||||
const next = setEvents.mock.calls[0][0]({});
|
||||
const [entry] = Object.values(next);
|
||||
expect(entry.title).toBe("Show");
|
||||
expect(entry.additional).toBe("S1 E2");
|
||||
expect(entry.url).toBe("https://sonarr.example/series/show");
|
||||
expect(entry.isCompleted).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { DateTime } from "luxon";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { EventStub, compareDateTimezoneStub } = vi.hoisted(() => ({
|
||||
EventStub: vi.fn(({ event }) => <div data-testid="event">{event.title}</div>),
|
||||
compareDateTimezoneStub: vi.fn(
|
||||
(date, event) => date.startOf("day").toISODate() === event.date.startOf("day").toISODate(),
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("./event", () => ({
|
||||
default: EventStub,
|
||||
compareDateTimezone: compareDateTimezoneStub,
|
||||
}));
|
||||
|
||||
import Monthly from "./monthly";
|
||||
|
||||
describe("widgets/calendar/monthly", () => {
|
||||
it("renders an empty placeholder when showDate is not set", () => {
|
||||
const { container } = render(
|
||||
<Monthly
|
||||
service={{ widget: {} }}
|
||||
colorVariants={{}}
|
||||
events={{}}
|
||||
showDate={null}
|
||||
setShowDate={() => {}}
|
||||
currentDate={DateTime.now()}
|
||||
/>,
|
||||
);
|
||||
expect(container.textContent).toBe("");
|
||||
});
|
||||
|
||||
it("navigates months and renders day events", () => {
|
||||
const setShowDate = vi.fn();
|
||||
const showDate = DateTime.local(2099, 2, 15).startOf("day");
|
||||
const currentDate = DateTime.local(2099, 2, 4).startOf("day");
|
||||
const service = { widget: { maxEvents: 10, showTime: false } };
|
||||
|
||||
const events = {
|
||||
e1: { title: "Today Event", date: DateTime.local(2099, 2, 15, 10, 0), color: "zinc" },
|
||||
e2: { title: "Other Event", date: DateTime.local(2099, 2, 16, 10, 0), color: "zinc" },
|
||||
};
|
||||
|
||||
render(
|
||||
<Monthly
|
||||
service={service}
|
||||
colorVariants={{}}
|
||||
events={events}
|
||||
showDate={showDate}
|
||||
setShowDate={setShowDate}
|
||||
currentDate={currentDate}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText("Today Event")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Other Event")).toBeNull();
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: ">" }));
|
||||
expect(setShowDate).toHaveBeenCalled();
|
||||
expect(setShowDate.mock.calls[0][0].toISODate()).toBe(showDate.plus({ months: 1 }).startOf("day").toISODate());
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "<" }));
|
||||
expect(setShowDate.mock.calls[1][0].toISODate()).toBe(showDate.minus({ months: 1 }).startOf("day").toISODate());
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: showDate.toFormat("MMMM y") }));
|
||||
expect(setShowDate.mock.calls[2][0].toISODate()).toBe(currentDate.startOf("day").toISODate());
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,95 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import createMockRes from "test-utils/create-mock-res";
|
||||
|
||||
const { httpProxy, getServiceWidget, logger } = vi.hoisted(() => ({
|
||||
httpProxy: vi.fn(),
|
||||
getServiceWidget: vi.fn(),
|
||||
logger: {
|
||||
debug: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("utils/logger", () => ({
|
||||
default: () => logger,
|
||||
}));
|
||||
|
||||
vi.mock("utils/config/service-helpers", () => ({
|
||||
default: getServiceWidget,
|
||||
}));
|
||||
|
||||
vi.mock("utils/proxy/http", () => ({
|
||||
httpProxy,
|
||||
}));
|
||||
|
||||
import calendarProxyHandler from "./proxy";
|
||||
|
||||
describe("widgets/calendar/proxy", () => {
|
||||
const envVersion = process.env.NEXT_PUBLIC_VERSION;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
process.env.NEXT_PUBLIC_VERSION = envVersion;
|
||||
});
|
||||
|
||||
it("returns 400 when integration is missing", async () => {
|
||||
getServiceWidget.mockResolvedValue({ integrations: [] });
|
||||
|
||||
const req = { query: { group: "g", service: "svc", endpoint: "foo", index: "0" } };
|
||||
const res = createMockRes();
|
||||
|
||||
await calendarProxyHandler(req, res);
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body).toEqual({ error: "Invalid integration" });
|
||||
});
|
||||
|
||||
it("returns 403 when integration has no URL", async () => {
|
||||
getServiceWidget.mockResolvedValue({ integrations: [{ name: "foo", url: "" }] });
|
||||
|
||||
const req = { query: { group: "g", service: "svc", endpoint: "foo", index: "0" } };
|
||||
const res = createMockRes();
|
||||
|
||||
await calendarProxyHandler(req, res);
|
||||
|
||||
expect(res.statusCode).toBe(403);
|
||||
expect(res.body).toEqual({ error: "No integration URL specified" });
|
||||
});
|
||||
|
||||
it("adds a User-Agent for Outlook integrations and returns string data", async () => {
|
||||
process.env.NEXT_PUBLIC_VERSION = "1.2.3";
|
||||
getServiceWidget.mockResolvedValue({
|
||||
integrations: [{ name: "outlook", url: "https://example.com/outlook.ics" }],
|
||||
});
|
||||
|
||||
httpProxy.mockResolvedValueOnce([200, "text/calendar", Buffer.from("CAL")]);
|
||||
|
||||
const req = { query: { group: "g", service: "svc", endpoint: "outlook", index: "0" } };
|
||||
const res = createMockRes();
|
||||
|
||||
await calendarProxyHandler(req, res);
|
||||
|
||||
expect(httpProxy).toHaveBeenCalledWith("https://example.com/outlook.ics", {
|
||||
headers: { "User-Agent": "gethomepage/1.2.3" },
|
||||
});
|
||||
expect(res.setHeader).toHaveBeenCalledWith("Content-Type", "text/calendar");
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body).toEqual({ data: "CAL" });
|
||||
});
|
||||
|
||||
it("passes through non-200 status codes from integrations", async () => {
|
||||
getServiceWidget.mockResolvedValue({
|
||||
integrations: [{ name: "foo", url: "https://example.com/foo.ics" }],
|
||||
});
|
||||
|
||||
httpProxy.mockResolvedValueOnce([503, "text/plain", Buffer.from("nope")]);
|
||||
|
||||
const req = { query: { group: "g", service: "svc", endpoint: "foo", index: "0" } };
|
||||
const res = createMockRes();
|
||||
|
||||
await calendarProxyHandler(req, res);
|
||||
|
||||
expect(res.statusCode).toBe(503);
|
||||
expect(res.body).toEqual(Buffer.from("nope"));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { expectWidgetConfigShape } from "test-utils/widget-config";
|
||||
|
||||
import widget from "./widget";
|
||||
|
||||
describe("calendar widget config", () => {
|
||||
it("exports a valid widget config", () => {
|
||||
expectWidgetConfigShape(widget);
|
||||
expect(widget.api).toBe("{url}");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user