import { cleanup, render, screen } from "@testing-library/react";
import { afterEach, describe, expect, it } from "vitest";
import { pluginRegistry } from "@/lib/plugins/registry";
import type { PluginRouteRegistration } from "@/lib/plugins/registry";
import { PluginPageFrame, resolvePluginPageChrome } from "./plugin-page";
const PLUGIN_ID = "plugin-a";
const PLUGIN_PATH = "/plugins/hello";
const PLUGIN_NAME = "Hello Plugin";
function Page() {
return null;
}
function registration(options?: PluginRouteRegistration["options"]): PluginRouteRegistration {
return { pluginId: PLUGIN_ID, path: PLUGIN_PATH, Component: Page, options };
}
describe("resolvePluginPageChrome", () => {
it("returns null when the registration opted out via topbar: false", () => {
expect(resolvePluginPageChrome(registration({ topbar: false }), [], "Hello")).toBeNull();
});
it("derives the title from the nav item registered for the same path", () => {
const chrome = resolvePluginPageChrome(
registration(),
[{ id: "nav", label: "Hello Nav", path: PLUGIN_PATH, icon: "ticket" }],
PLUGIN_NAME,
);
expect(chrome).toMatchObject({ title: "Hello Nav", icon: "ticket" });
});
it("falls back to the plugin display name, then the route path", () => {
expect(resolvePluginPageChrome(registration(), [], PLUGIN_NAME)?.title).toBe(PLUGIN_NAME);
expect(resolvePluginPageChrome(registration(), [], undefined)?.title).toBe(PLUGIN_PATH);
});
it("prefers explicit chrome config over every derived default", () => {
const chrome = resolvePluginPageChrome(
registration({
topbar: { title: "Custom", subtitle: "Sub", icon: "chart", backHref: "/x", backLabel: "X" },
}),
[{ id: "nav", label: "Nav Label", path: PLUGIN_PATH, icon: "ticket" }],
PLUGIN_NAME,
);
expect(chrome).toEqual({
title: "Custom",
subtitle: "Sub",
icon: "chart",
backHref: "/x",
backLabel: "X",
Actions: undefined,
});
});
});
describe("PluginPageFrame", () => {
afterEach(() => {
cleanup();
pluginRegistry.unregisterPlugin(PLUGIN_ID);
});
it("renders a kandev topbar with the derived title by default", () => {
pluginRegistry.forPlugin(PLUGIN_ID, PLUGIN_NAME);
render(
,
);
expect(screen.getByRole("banner")).not.toBeNull();
expect(screen.getByText(PLUGIN_NAME)).not.toBeNull();
expect(screen.getByTestId("plugin-content")).not.toBeNull();
});
it("renders the children bare when the route opted out of the topbar", () => {
render(
,
);
expect(screen.queryByRole("banner")).toBeNull();
expect(screen.getByTestId("plugin-content")).not.toBeNull();
});
it("renders configured subtitle and a plugin-provided actions component", () => {
function Actions() {
return ;
}
render(
,
);
expect(screen.getByText("Custom")).not.toBeNull();
expect(screen.getByText("Tickets")).not.toBeNull();
expect(screen.getByTestId("plugin-topbar-action")).not.toBeNull();
});
});