test: add widget shared component coverage

This commit is contained in:
shamoon
2026-02-04 07:50:26 -08:00
parent b109051af7
commit 04c95bd0d9
17 changed files with 474 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
// @vitest-environment jsdom
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import Node from "./node";
describe("components/widgets/kubernetes/node", () => {
it("renders cluster label when showLabel is enabled", () => {
const data = { cpu: { percent: 50 }, memory: { free: 123, percent: 10 } };
const { container } = render(<Node type="cluster" options={{ showLabel: true, label: "Cluster A" }} data={data} />);
expect(screen.getByText("50")).toBeInTheDocument();
expect(screen.getByText("123")).toBeInTheDocument();
expect(screen.getByText("Cluster A")).toBeInTheDocument();
expect(container.querySelectorAll('div[style*="width:"]').length).toBeGreaterThan(0);
});
it("renders node name when showLabel is enabled for node type", () => {
const data = { name: "node-1", ready: true, cpu: { percent: 1 }, memory: { free: 2, percent: 3 } };
render(<Node type="node" options={{ showLabel: true }} data={data} />);
expect(screen.getByText("node-1")).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,32 @@
// @vitest-environment jsdom
import { render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
const { Resource } = vi.hoisted(() => ({
Resource: vi.fn(({ children }) => <div data-testid="lh-resource">{children}</div>),
}));
vi.mock("../widget/resource", () => ({
default: Resource,
}));
vi.mock("../widget/widget_label", () => ({
default: ({ label }) => <div data-testid="lh-label">{label}</div>,
}));
import Node from "./node";
describe("components/widgets/longhorn/node", () => {
it("passes calculated percentage and renders label when enabled", () => {
const data = { node: { id: "n1", available: 25, maximum: 100 } };
render(<Node data={{ node: data.node }} expanded labels />);
expect(Resource).toHaveBeenCalledTimes(1);
const callProps = Resource.mock.calls[0][0];
expect(callProps.percentage).toBe(75);
expect(callProps.expanded).toBe(true);
expect(screen.getByTestId("lh-label")).toHaveTextContent("n1");
});
});

View File

@@ -0,0 +1,20 @@
// @vitest-environment jsdom
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import QueueEntry from "./queueEntry";
describe("components/widgets/queue/queueEntry", () => {
it("renders title and progress width", () => {
const { container } = render(
<QueueEntry title="Download" activity="Downloading" timeLeft="1m" progress={42} size="1GB" />,
);
expect(screen.getByText("Download")).toBeInTheDocument();
expect(screen.getByText("1GB - Downloading - 1m")).toBeInTheDocument();
const bar = container.querySelector("div[style]");
expect(bar.style.width).toBe("42%");
});
});

View File

@@ -0,0 +1,18 @@
// @vitest-environment jsdom
import { render } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import UsageBar from "./usage-bar";
describe("components/widgets/resources/usage-bar", () => {
it("normalizes percent to [0, 100] and applies width style", () => {
const { container: c0 } = render(<UsageBar percent={-5} />);
const inner0 = c0.querySelector("div > div > div");
expect(inner0.style.width).toBe("0%");
const { container: c1 } = render(<UsageBar percent={150} />);
const inner1 = c1.querySelector("div > div > div");
expect(inner1.style.width).toBe("100%");
});
});

View File

@@ -0,0 +1,58 @@
// @vitest-environment jsdom
import { render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
const { dynamic } = vi.hoisted(() => {
const dynamic = vi.fn((loader, opts) => {
const loaderStr = loader.toString();
const ssr = opts?.ssr === false ? "false" : "true";
return function DynamicWidget({ options }) {
return (
<div
data-testid="dynamic-widget"
data-loader={loaderStr}
data-ssr={ssr}
data-options={JSON.stringify(options)}
/>
);
};
});
return { dynamic };
});
vi.mock("next/dynamic", () => ({
default: dynamic,
}));
vi.mock("components/errorboundry", () => ({
default: ({ children }) => <div data-testid="error-boundary">{children}</div>,
}));
import Widget from "./widget";
describe("components/widgets/widget", () => {
it("renders the mapped widget component and forwards style into options", () => {
render(
<Widget widget={{ type: "search", options: { provider: ["google"] } }} style={{ header: "boxedWidgets" }} />,
);
const boundary = screen.getByTestId("error-boundary");
expect(boundary).toBeInTheDocument();
const el = screen.getByTestId("dynamic-widget");
expect(el.getAttribute("data-loader")).toContain("search/search");
const forwarded = JSON.parse(el.getAttribute("data-options"));
expect(forwarded.provider).toEqual(["google"]);
expect(forwarded.style).toEqual({ header: "boxedWidgets" });
});
it("renders a missing message when widget type is unknown", () => {
render(<Widget widget={{ type: "nope", options: {} }} style={{}} />);
expect(screen.getByText("Missing")).toBeInTheDocument();
expect(screen.getByText("nope")).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,76 @@
// @vitest-environment jsdom
import { screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { renderWithProviders } from "test-utils/render-with-providers";
import Container, { getAllClasses } from "./container";
import PrimaryText from "./primary_text";
import Raw from "./raw";
import SecondaryText from "./secondary_text";
import WidgetIcon from "./widget_icon";
function FakeIcon(props) {
return <svg data-testid="fake-icon" {...props} />;
}
describe("components/widgets/widget/container", () => {
it("getAllClasses supports boxedWidgets + cardBlur and right alignment", () => {
const boxed = getAllClasses({ style: { header: "boxedWidgets", cardBlur: "md" } }, "x");
expect(boxed).toContain("backdrop-blur-md");
expect(boxed).toContain("x");
const right = getAllClasses({ style: { isRightAligned: true } }, "y");
expect(right).toContain("justify-center");
expect(right).toContain("y");
expect(right).not.toContain("max-w:full");
});
it("renders an anchor when href is provided and prefers options.target over settings.target", () => {
renderWithProviders(
<Container options={{ href: "http://example", target: "_self" }}>
<WidgetIcon icon={FakeIcon} />
<PrimaryText>P</PrimaryText>
<SecondaryText>S</SecondaryText>
<Raw>
<div data-testid="bottom">B</div>
</Raw>
</Container>,
{ settings: { target: "_blank" } },
);
const link = screen.getByRole("link");
expect(link.getAttribute("href")).toBe("http://example");
expect(link.getAttribute("target")).toBe("_self");
expect(screen.getByTestId("fake-icon")).toBeInTheDocument();
expect(screen.getByText("P")).toBeInTheDocument();
expect(screen.getByText("S")).toBeInTheDocument();
expect(screen.getByTestId("bottom")).toBeInTheDocument();
});
it("renders only bottom content when children are a single Raw element", () => {
const { container } = renderWithProviders(
<Container options={{}}>
<Raw>
<div data-testid="only-bottom">B</div>
</Raw>
</Container>,
{ settings: { target: "_self" } },
);
expect(container.querySelector(".widget-inner")).toBeNull();
expect(screen.getByTestId("only-bottom")).toBeInTheDocument();
});
it("does not crash when clicked (href case is normal link)", () => {
renderWithProviders(
<Container options={{ href: "http://example" }}>
<Raw>
<div>Bottom</div>
</Raw>
</Container>,
{ settings: { target: "_self" } },
);
});
});

View File

@@ -0,0 +1,23 @@
// @vitest-environment jsdom
import { fireEvent, render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import ContainerButton from "./container_button";
import Raw from "./raw";
describe("components/widgets/widget/container_button", () => {
it("invokes callback on click", () => {
const cb = vi.fn();
render(
<ContainerButton options={{}} callback={cb}>
<Raw>
<div>child</div>
</Raw>
</ContainerButton>,
);
fireEvent.click(screen.getByRole("button"));
expect(cb).toHaveBeenCalledTimes(1);
});
});

View File

@@ -0,0 +1,23 @@
// @vitest-environment jsdom
import { fireEvent, render } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import ContainerForm from "./container_form";
describe("components/widgets/widget/container_form", () => {
it("calls callback on submit", () => {
const cb = vi.fn((e) => e.preventDefault());
const { container } = render(
<ContainerForm options={{}} callback={cb}>
{[<div key="c">child</div>]}
</ContainerForm>,
);
const form = container.querySelector("form");
fireEvent.submit(form);
expect(cb).toHaveBeenCalledTimes(1);
});
});

View File

@@ -0,0 +1,24 @@
// @vitest-environment jsdom
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import ContainerLink from "./container_link";
import Raw from "./raw";
describe("components/widgets/widget/container_link", () => {
it("renders an anchor using href or url", () => {
const { rerender } = render(<ContainerLink options={{ href: "http://a" }} target="_self" />);
expect(screen.getByRole("link").getAttribute("href")).toBe("http://a");
expect(screen.getByRole("link").getAttribute("target")).toBe("_self");
rerender(
<ContainerLink options={{ url: "http://b" }} target="_blank">
<Raw>
<div>child</div>
</Raw>
</ContainerLink>,
);
expect(screen.getByRole("link").getAttribute("href")).toBe("http://b");
});
});

View File

@@ -0,0 +1,15 @@
// @vitest-environment jsdom
import { screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { renderWithProviders } from "test-utils/render-with-providers";
import Error from "./error";
describe("components/widgets/widget/error", () => {
it("renders the api_error message", () => {
renderWithProviders(<Error options={{}} />, { settings: { target: "_self" } });
expect(screen.getByText("widget.api_error")).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,13 @@
// @vitest-environment jsdom
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import PrimaryText from "./primary_text";
describe("components/widgets/widget/primary_text", () => {
it("renders children", () => {
render(<PrimaryText>hello</PrimaryText>);
expect(screen.getByText("hello")).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,20 @@
// @vitest-environment jsdom
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import Raw from "./raw";
describe("components/widgets/widget/raw", () => {
it("renders nested Raw content", () => {
render(
<Raw>
<Raw>
<div>inner</div>
</Raw>
</Raw>,
);
expect(screen.getByText("inner")).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,38 @@
// @vitest-environment jsdom
import { render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
const { UsageBar } = vi.hoisted(() => ({
UsageBar: vi.fn(({ percent }) => <div data-testid="usagebar" data-percent={String(percent)} />),
}));
vi.mock("../resources/usage-bar", () => ({
default: UsageBar,
}));
import Resource from "./resource";
function FakeIcon(props) {
return <svg data-testid="resource-icon" {...props} />;
}
describe("components/widgets/widget/resource", () => {
it("renders icon/value/label and shows usage bar when percentage is set", () => {
render(<Resource icon={FakeIcon} value="v" label="l" percentage={0} />);
expect(screen.getByTestId("resource-icon")).toBeInTheDocument();
expect(screen.getByText("v")).toBeInTheDocument();
expect(screen.getByText("l")).toBeInTheDocument();
expect(screen.getByTestId("usagebar").getAttribute("data-percent")).toBe("0");
});
it("renders expanded values when expanded", () => {
render(
<Resource icon={FakeIcon} value="v" label="l" expanded expandedValue="ev" expandedLabel="el" percentage={10} />,
);
expect(screen.getByText("ev")).toBeInTheDocument();
expect(screen.getByText("el")).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,31 @@
// @vitest-environment jsdom
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import Resource from "./resource";
import Resources from "./resources";
import WidgetLabel from "./widget_label";
function FakeIcon() {
return <svg />;
}
describe("components/widgets/widget/resources", () => {
it("filters children to Resource + WidgetLabel and wraps them in a link", () => {
render(
<Resources options={{ href: "http://example" }} target="_self" additionalClassNames="x">
{[
<Resource key="r" icon={FakeIcon} value="v" label="l" />,
<WidgetLabel key="w" label="Label" />,
<div key="o">Other</div>,
]}
</Resources>,
);
expect(screen.getByRole("link").getAttribute("href")).toBe("http://example");
expect(screen.getByText("v")).toBeInTheDocument();
expect(screen.getByText("Label")).toBeInTheDocument();
expect(screen.queryByText("Other")).toBeNull();
});
});

View File

@@ -0,0 +1,13 @@
// @vitest-environment jsdom
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import SecondaryText from "./secondary_text";
describe("components/widgets/widget/secondary_text", () => {
it("renders children", () => {
render(<SecondaryText>world</SecondaryText>);
expect(screen.getByText("world")).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,30 @@
// @vitest-environment jsdom
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import WidgetIcon from "./widget_icon";
function FakeIcon(props) {
return <svg data-testid="icon" {...props} />;
}
describe("components/widgets/widget/widget_icon", () => {
it("applies size classes and pulse animation", () => {
render(
<>
<WidgetIcon icon={FakeIcon} size="s" />
<WidgetIcon icon={FakeIcon} size="m" />
<WidgetIcon icon={FakeIcon} size="l" pulse />
<WidgetIcon icon={FakeIcon} size="xl" />
</>,
);
const icons = screen.getAllByTestId("icon");
expect(icons[0].getAttribute("class")).toContain("w-5 h-5");
expect(icons[1].getAttribute("class")).toContain("w-6 h-6");
expect(icons[2].getAttribute("class")).toContain("w-8 h-8");
expect(icons[2].getAttribute("class")).toContain("animate-pulse");
expect(icons[3].getAttribute("class")).toContain("w-10 h-10");
});
});

View File

@@ -0,0 +1,13 @@
// @vitest-environment jsdom
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import WidgetLabel from "./widget_label";
describe("components/widgets/widget/widget_label", () => {
it("renders label text", () => {
render(<WidgetLabel label="Label A" />);
expect(screen.getByText("Label A")).toBeInTheDocument();
});
});