tftsr-devops_investigation/tests/unit/Hotbar.test.tsx

88 lines
2.2 KiB
TypeScript
Raw Normal View History

fix(kube): bridge kubeconfig storage to in-memory cluster map and fix UI issues Resolves four bugs in the Kubernetes management interface: 1. **Cluster not found error** - commands/kube.rs::list_nodes (and all other kube resource commands) look up clusters from state.clusters (in-memory map) which was never populated from the kubeconfig_files table. Add a new connect_cluster_from_kubeconfig Tauri command that reads the encrypted kubeconfig from the DB, decrypts it, and inserts a ClusterClient into state.clusters. Wire it into KubernetesPage on initial load and cluster change so the in-memory map is always populated before any kube command runs. 2. **Dropdown selection has no effect** - same root cause as #1; activating a kubeconfig only updated the DB flag but never loaded the client into memory. handleClusterChange now calls connectClusterFromKubeconfigCmd after activation. 3. **GUID shown instead of cluster name** - ClusterOverview displayed the raw internal UUID as the page subtitle. Now accepts a clusterName prop (populated from kubeconfig.context) and renders that instead. ClusterDetails similarly changed to show kubeconfig.context in the header, not the UUID. 4. **Bell icon not clickable** - Hotbar bell button had no onClick handler. Add optional onNotifications / notificationCount props; badge count is now dynamic rather than hardcoded. KubernetesPage wires up a notifications dialog showing active cluster context and a link to the Events section. All changes follow TDD: failing tests written first, then implementation.
2026-06-07 22:39:07 +00:00
import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import { Hotbar } from "@/components/Kubernetes/Hotbar";
// Mock zustand's useStore so Hotbar can render without a real store
vi.mock("zustand", () => ({
useStore: vi.fn((_store: unknown, selector: (s: unknown) => unknown) =>
selector({ clusters: [], selectedClusterId: null })
),
}));
vi.mock("@/stores/kubernetesStore", () => ({
useKubernetesStore: vi.fn(),
}));
describe("Hotbar", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("renders without error", () => {
render(
<Hotbar
onRefresh={vi.fn()}
onAddResource={vi.fn()}
onSettings={vi.fn()}
/>
);
expect(screen.getByRole("button", { name: /notification/i })).toBeInTheDocument();
});
it("calls onNotifications when bell icon is clicked", () => {
const onNotifications = vi.fn();
render(
<Hotbar
onRefresh={vi.fn()}
onAddResource={vi.fn()}
onSettings={vi.fn()}
onNotifications={onNotifications}
/>
);
fireEvent.click(screen.getByRole("button", { name: /notification/i }));
expect(onNotifications).toHaveBeenCalledTimes(1);
});
it("renders bell button even without onNotifications prop", () => {
render(
<Hotbar
onRefresh={vi.fn()}
onAddResource={vi.fn()}
onSettings={vi.fn()}
/>
);
const bellButton = screen.getByRole("button", { name: /notification/i });
expect(bellButton).toBeInTheDocument();
expect(() => fireEvent.click(bellButton)).not.toThrow();
});
it("shows notification count badge when notificationCount is provided", () => {
render(
<Hotbar
onRefresh={vi.fn()}
onAddResource={vi.fn()}
onSettings={vi.fn()}
notificationCount={5}
/>
);
expect(screen.getByText("5")).toBeInTheDocument();
});
it("hides badge when notificationCount is zero", () => {
render(
<Hotbar
onRefresh={vi.fn()}
onAddResource={vi.fn()}
onSettings={vi.fn()}
notificationCount={0}
/>
);
expect(screen.queryByText("0")).not.toBeInTheDocument();
});
});