Some checks failed
PR Review Automation / review (pull_request) Has been cancelled
Test / frontend-tests (pull_request) Successful in 1m37s
Test / frontend-typecheck (pull_request) Successful in 1m46s
Test / rust-fmt-check (pull_request) Failing after 10m52s
Test / rust-clippy (pull_request) Successful in 12m34s
Test / rust-tests (pull_request) Successful in 14m8s
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.
88 lines
2.2 KiB
TypeScript
88 lines
2.2 KiB
TypeScript
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();
|
|
});
|
|
});
|