mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 22:55:24 +00:00
feat: frame host for running one plugin view outside the shell
This commit is contained in:
1 parent
bb448b6753
commit
594fc19cbf
7 files changed
+532
-6
No files matched your search
@@ -0,0 +1,174 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { act, screen, waitFor } from "@testing-library/react";
|
||||
import { useHosts, useTabs } from "@termix-ssh/plugin-sdk/frontend";
|
||||
import type { PluginSummary } from "@/api/plugins-api";
|
||||
|
||||
vi.mock("@/main-axios", async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import("@/main-axios")>()),
|
||||
getSSHHosts: vi.fn(async () => [
|
||||
{ id: 7, name: "web-01", ip: "10.0.0.7", port: 22, username: "root" },
|
||||
]),
|
||||
}));
|
||||
|
||||
import {
|
||||
startPluginFrame,
|
||||
type FrameShell,
|
||||
type PluginFrame,
|
||||
} from "@/plugin-host/frame-host";
|
||||
import { resetPluginStore } from "@/plugin-host/plugin-store";
|
||||
import { resetTabTypes } from "@/shell/tab-registry";
|
||||
|
||||
const summary: PluginSummary = {
|
||||
id: "gizmo",
|
||||
name: "Gizmo",
|
||||
version: "1.0.0",
|
||||
enabled: true,
|
||||
state: "active",
|
||||
frontend: true,
|
||||
contributes: {
|
||||
tabs: [
|
||||
{
|
||||
id: "gizmo-home",
|
||||
titleKey: "nav.home",
|
||||
icon: "Box",
|
||||
openFrom: ["rail"],
|
||||
},
|
||||
{
|
||||
id: "gizmo-host",
|
||||
titleKey: "nav.host",
|
||||
icon: "Server",
|
||||
openFrom: ["host-context-menu"],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
function Home() {
|
||||
const { hosts } = useHosts();
|
||||
const tabs = useTabs();
|
||||
return (
|
||||
<div>
|
||||
<p>hosts: {hosts.map((host) => host.name).join(",")}</p>
|
||||
<button onClick={() => tabs.connectHost(hosts[0] as never)}>
|
||||
connect
|
||||
</button>
|
||||
<button onClick={() => tabs.openTab(hosts[0] as never, "gizmo-host")}>
|
||||
open
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function HostView({ host }: { host?: { name: string } }) {
|
||||
return <p>host view: {host?.name ?? "none"}</p>;
|
||||
}
|
||||
|
||||
const module = {
|
||||
activate: (app: {
|
||||
registerTab: (type: string, component: unknown, options?: object) => void;
|
||||
}) => {
|
||||
app.registerTab("gizmo-home", Home, { hostless: true });
|
||||
app.registerTab("gizmo-host", HostView, { requiresHost: true });
|
||||
},
|
||||
deactivate: vi.fn(),
|
||||
};
|
||||
|
||||
function makeShell(): FrameShell & { calls: unknown[][] } {
|
||||
const calls: unknown[][] = [];
|
||||
const record =
|
||||
(name: string) =>
|
||||
(...args: unknown[]) => {
|
||||
calls.push([name, ...args]);
|
||||
};
|
||||
return {
|
||||
calls,
|
||||
openTab: record("openTab"),
|
||||
openSingletonTab: record("openSingletonTab"),
|
||||
connectHost: record("connectHost"),
|
||||
closeTab: record("closeTab"),
|
||||
renameTab: record("renameTab"),
|
||||
};
|
||||
}
|
||||
|
||||
let root: HTMLElement;
|
||||
let frame: PluginFrame | null = null;
|
||||
|
||||
beforeEach(() => {
|
||||
root = document.createElement("div");
|
||||
document.body.appendChild(root);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (frame) await act(() => frame!.dispose());
|
||||
frame = null;
|
||||
root.remove();
|
||||
resetPluginStore();
|
||||
resetTabTypes();
|
||||
});
|
||||
|
||||
describe("startPluginFrame", () => {
|
||||
it("renders the chosen view with the server's hosts", async () => {
|
||||
const shell = makeShell();
|
||||
frame = await act(() =>
|
||||
startPluginFrame({
|
||||
root,
|
||||
summary,
|
||||
module: module as never,
|
||||
view: { view: "gizmo-home" },
|
||||
theme: { id: "dracula", mode: "dark" },
|
||||
shell,
|
||||
}),
|
||||
);
|
||||
await waitFor(() => expect(screen.getByText("hosts: web-01")).toBeTruthy());
|
||||
expect(document.documentElement.classList.contains("dracula")).toBe(true);
|
||||
expect(frame.views()).toEqual({
|
||||
tabs: ["gizmo-home", "gizmo-host"],
|
||||
panels: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("passes shell calls to the embedder by host id", async () => {
|
||||
const shell = makeShell();
|
||||
frame = await act(() =>
|
||||
startPluginFrame({
|
||||
root,
|
||||
summary,
|
||||
module: module as never,
|
||||
view: { view: "gizmo-home" },
|
||||
theme: { id: "light", mode: "light" },
|
||||
shell,
|
||||
}),
|
||||
);
|
||||
await waitFor(() => screen.getByText("hosts: web-01"));
|
||||
act(() => screen.getByText("connect").click());
|
||||
act(() => screen.getByText("open").click());
|
||||
expect(shell.calls).toEqual([
|
||||
["connectHost", "7", undefined],
|
||||
["openTab", "7", "gizmo-host", { label: undefined, data: undefined }],
|
||||
]);
|
||||
});
|
||||
|
||||
it("switches views and themes, and deactivates on dispose", async () => {
|
||||
frame = await act(() =>
|
||||
startPluginFrame({
|
||||
root,
|
||||
summary,
|
||||
module: module as never,
|
||||
view: { view: "gizmo-host", hostId: "7" },
|
||||
theme: { id: "dark", mode: "dark" },
|
||||
shell: makeShell(),
|
||||
}),
|
||||
);
|
||||
await waitFor(() => screen.getByText("host view: web-01"));
|
||||
act(() => frame!.setView({ view: "gizmo-home" }));
|
||||
await waitFor(() => screen.getByText("hosts: web-01"));
|
||||
act(() => frame!.setTheme({ id: "nord", mode: "dark" }));
|
||||
await waitFor(() =>
|
||||
expect(document.documentElement.classList.contains("nord")).toBe(true),
|
||||
);
|
||||
await act(() => frame!.dispose());
|
||||
frame = null;
|
||||
expect(module.deactivate).toHaveBeenCalled();
|
||||
expect(root.textContent).toBe("");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user