import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import { invoke } from "@tauri-apps/api/core";
import { KubernetesPage } from "@/pages/Kubernetes/KubernetesPage";
import { useKubernetesStore } from "@/stores/kubernetesStore";
// Mock all Kubernetes child components that do their own invoke calls or have heavy deps
vi.mock("@/components/Kubernetes/ClusterOverview", () => ({
ClusterOverview: ({ clusterId }: { clusterId: string }) => (
ClusterOverview:{clusterId}
),
}));
vi.mock("@/components/Kubernetes/PodList", () => ({
PodList: () => PodList
,
}));
vi.mock("@/components/Kubernetes/DeploymentList", () => ({
DeploymentList: () => DeploymentList
,
}));
vi.mock("@/components/Kubernetes/DaemonSetList", () => ({
DaemonSetList: () => DaemonSetList
,
}));
vi.mock("@/components/Kubernetes/StatefulSetList", () => ({
StatefulSetList: () => StatefulSetList
,
}));
vi.mock("@/components/Kubernetes/ReplicaSetList", () => ({
ReplicaSetList: () => ReplicaSetList
,
}));
vi.mock("@/components/Kubernetes/JobList", () => ({
JobList: () => JobList
,
}));
vi.mock("@/components/Kubernetes/CronJobList", () => ({
CronJobList: () => CronJobList
,
}));
vi.mock("@/components/Kubernetes/ServiceList", () => ({
ServiceList: () => ServiceList
,
}));
vi.mock("@/components/Kubernetes/IngressList", () => ({
IngressList: () => IngressList
,
}));
vi.mock("@/components/Kubernetes/ConfigMapList", () => ({
ConfigMapList: () => ConfigMapList
,
}));
vi.mock("@/components/Kubernetes/SecretList", () => ({
SecretList: () => SecretList
,
}));
vi.mock("@/components/Kubernetes/HPAList", () => ({
HPAList: () => HPAList
,
}));
vi.mock("@/components/Kubernetes/PVCList", () => ({
PVCList: () => PVCList
,
}));
vi.mock("@/components/Kubernetes/PVList", () => ({
PVList: () => PVList
,
}));
vi.mock("@/components/Kubernetes/ServiceAccountList", () => ({
ServiceAccountList: () => ServiceAccountList
,
}));
vi.mock("@/components/Kubernetes/RoleList", () => ({
RoleList: () => RoleList
,
}));
vi.mock("@/components/Kubernetes/ClusterRoleList", () => ({
ClusterRoleList: () => ClusterRoleList
,
}));
vi.mock("@/components/Kubernetes/RoleBindingList", () => ({
RoleBindingList: () => RoleBindingList
,
}));
vi.mock("@/components/Kubernetes/ClusterRoleBindingList", () => ({
ClusterRoleBindingList: () => ClusterRoleBindingList
,
}));
vi.mock("@/components/Kubernetes/NodeList", () => ({
NodeList: () => NodeList
,
}));
vi.mock("@/components/Kubernetes/EventList", () => ({
EventList: () => EventList
,
}));
vi.mock("@/components/Kubernetes/PortForwardList", () => ({
PortForwardList: ({ onStart }: { onStart: () => void }) => (
),
}));
vi.mock("@/components/Kubernetes/PortForwardForm", () => ({
PortForwardForm: ({ isOpen }: { isOpen: boolean }) =>
isOpen ? PortForwardForm
: null,
}));
vi.mock("@/components/Kubernetes/CommandPalette", () => ({
CommandPalette: ({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) =>
isOpen ? (
CommandPalette
) : null,
}));
vi.mock("@/components/Kubernetes/Hotbar", () => ({
Hotbar: ({ onRefresh }: { onRefresh: () => void }) => (
),
}));
type MockedInvoke = ReturnType;
const mockInvoke = invoke as unknown as MockedInvoke;
const MOCK_KUBECONFIGS = [
{ id: "kc-1", name: "prod-cluster", context: "prod", cluster_url: "https://k8s.prod.example.com", is_active: true },
{ id: "kc-2", name: "staging-cluster", context: "staging", cluster_url: "https://k8s.staging.example.com", is_active: false },
];
const MOCK_NAMESPACES = [
{ name: "default", status: "Active", age: "100d" },
{ name: "kube-system", status: "Active", age: "100d" },
];
function renderPage() {
return render(
);
}
describe("KubernetesPage", () => {
beforeEach(() => {
vi.clearAllMocks();
// Reset the kubernetes store to a clean state
useKubernetesStore.setState({
selectedClusterId: null,
selectedNamespace: "all",
});
// Default: return empty arrays for all IPC calls unless overridden
mockInvoke.mockImplementation((cmd: string) => {
if (cmd === "list_kubeconfigs") return Promise.resolve([]);
if (cmd === "list_namespaces") return Promise.resolve([]);
if (cmd === "list_port_forwards") return Promise.resolve([]);
return Promise.resolve([]);
});
});
describe("Sidebar structure", () => {
it("renders all resource category section headings", async () => {
renderPage();
await waitFor(() => {
expect(screen.getByText("Workloads")).toBeInTheDocument();
expect(screen.getByText("Network")).toBeInTheDocument();
expect(screen.getByText("Config")).toBeInTheDocument();
expect(screen.getByText("Storage")).toBeInTheDocument();
expect(screen.getByText("Access Control")).toBeInTheDocument();
expect(screen.getByText("Cluster")).toBeInTheDocument();
});
});
it("renders all Workloads nav items", async () => {
renderPage();
await waitFor(() => {
expect(screen.getByRole("button", { name: "Pods" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Deployments" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Daemon Sets" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Stateful Sets" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Replica Sets" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Jobs" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Cron Jobs" })).toBeInTheDocument();
});
});
it("renders all Network nav items", async () => {
renderPage();
await waitFor(() => {
expect(screen.getByRole("button", { name: "Services" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Ingresses" })).toBeInTheDocument();
});
});
it("renders all Config & Storage nav items", async () => {
renderPage();
await waitFor(() => {
expect(screen.getByRole("button", { name: "Config Maps" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Secrets" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Horizontal Pod Autoscalers" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Persistent Volume Claims" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Persistent Volumes" })).toBeInTheDocument();
});
});
it("renders all Access Control nav items", async () => {
renderPage();
await waitFor(() => {
expect(screen.getByRole("button", { name: "Service Accounts" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Roles" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Cluster Roles" })).toBeInTheDocument();
// Use exact aria-label to disambiguate from "Cluster Role Bindings"
expect(screen.getByRole("button", { name: "Role Bindings" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Cluster Role Bindings" })).toBeInTheDocument();
});
});
it("renders Port Forwarding under Cluster section", async () => {
renderPage();
await waitFor(() => {
expect(screen.getByRole("button", { name: "Port Forwarding" })).toBeInTheDocument();
});
});
});
describe("Cluster selector", () => {
it("renders a cluster selector trigger button", async () => {
renderPage();
// The SelectTrigger renders as a