tftsr-devops_investigation/tests/unit/PodList.test.tsx
Shaun Arman 2a8183daf2 fix(lint): remove unused variables in test files
Remove unused import and variable in criticalUIFixes test
Update PodList test mocks to use new Interactive* modal components

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-06-09 13:36:36 -05:00

224 lines
6.1 KiB
TypeScript

import React from "react";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import { invoke } from "@tauri-apps/api/core";
import { PodList } from "@/components/Kubernetes/PodList";
import type { PodInfo } from "@/lib/tauriCommands";
vi.mock("@tauri-apps/api/core");
// Silence console.error noise from modal portals in jsdom
vi.mock("@/components/Kubernetes/LogStreamPanel", () => ({
LogStreamPanel: ({ namespace }: { namespace: string }) => (
<div data-testid="logs-modal" data-namespace={namespace} />
),
}));
vi.mock("@/components/Kubernetes/InteractiveShellModal", () => ({
InteractiveShellModal: ({ namespace }: { namespace: string }) => (
<div data-testid="shell-modal" data-namespace={namespace} />
),
}));
vi.mock("@/components/Kubernetes/InteractiveAttachModal", () => ({
InteractiveAttachModal: ({ namespace }: { namespace: string }) => (
<div data-testid="attach-modal" data-namespace={namespace} />
),
}));
vi.mock("@/components/Kubernetes/EditResourceModal", () => ({
EditResourceModal: ({ namespace }: { namespace: string }) => (
<div data-testid="edit-modal" data-namespace={namespace} />
),
}));
vi.mock("@/components/Kubernetes/ConfirmDeleteDialog", () => ({
ConfirmDeleteDialog: ({
onConfirm,
resourceName,
}: {
onConfirm: () => void;
resourceName: string;
}) => (
<div data-testid="confirm-delete">
<span>{resourceName}</span>
<button onClick={onConfirm}>confirm</button>
</div>
),
}));
type MockedInvoke = typeof invoke & {
mockResolvedValue: (v: unknown) => void;
mockRejectedValue: (e: Error) => void;
mockImplementation: (fn: (cmd: string) => Promise<unknown>) => void;
};
const mockInvoke = invoke as MockedInvoke;
// A pod whose own namespace ("default") differs from the filter prop ("all")
const mockPod: PodInfo = {
name: "test-pod",
namespace: "default",
status: "Running",
ready: "1/1",
age: "1h",
containers: ["app"],
};
function openActionMenu() {
const trigger = screen.getByRole("button", { name: /actions/i });
fireEvent.click(trigger);
}
describe("PodList — namespace isolation", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('Edit action calls getResourceYamlCmd with pod.namespace ("default"), not filter "all"', async () => {
mockInvoke.mockImplementation((cmd: string) => {
if (cmd === "get_resource_yaml") {
return Promise.resolve("apiVersion: v1\nkind: Pod");
}
return Promise.resolve(undefined);
});
render(
<PodList
pods={[mockPod]}
clusterId="c1"
namespace="all"
onRefresh={() => {}}
/>
);
openActionMenu();
fireEvent.click(screen.getByRole("button", { name: /^edit$/i }));
await waitFor(() => {
expect(mockInvoke).toHaveBeenCalledWith("get_resource_yaml", {
clusterId: "c1",
namespace: "default",
resourceType: "pods",
resourceName: "test-pod",
});
});
});
it('Delete action calls deleteResourceCmd with pod.namespace ("default"), not filter "all"', async () => {
mockInvoke.mockResolvedValue(undefined);
render(
<PodList
pods={[mockPod]}
clusterId="c1"
namespace="all"
onRefresh={() => {}}
/>
);
openActionMenu();
fireEvent.click(screen.getByRole("button", { name: /^delete$/i }));
await waitFor(() => {
expect(screen.getByTestId("confirm-delete")).toBeDefined();
});
fireEvent.click(screen.getByRole("button", { name: /confirm/i }));
await waitFor(() => {
expect(mockInvoke).toHaveBeenCalledWith("delete_resource", {
clusterId: "c1",
namespace: "default",
resourceType: "pods",
resourceName: "test-pod",
});
});
});
it('Force Delete action calls forceDeleteResourceCmd with pod.namespace ("default"), not filter "all"', async () => {
mockInvoke.mockResolvedValue(undefined);
// Force Delete is only visible when pod is Running or Pending
render(
<PodList
pods={[mockPod]}
clusterId="c1"
namespace="all"
onRefresh={() => {}}
/>
);
openActionMenu();
fireEvent.click(screen.getByRole("button", { name: /^force delete$/i }));
await waitFor(() => {
expect(screen.getByTestId("confirm-delete")).toBeDefined();
});
fireEvent.click(screen.getByRole("button", { name: /confirm/i }));
await waitFor(() => {
expect(mockInvoke).toHaveBeenCalledWith("force_delete_resource", {
clusterId: "c1",
namespace: "default",
resourceType: "pods",
resourceName: "test-pod",
});
});
});
it('Logs modal receives pod.namespace ("default"), not filter "all"', async () => {
render(
<PodList
pods={[mockPod]}
clusterId="c1"
namespace="all"
onRefresh={() => {}}
/>
);
openActionMenu();
fireEvent.click(screen.getByRole("button", { name: /^logs$/i }));
await waitFor(() => {
const modal = screen.getByTestId("logs-modal");
expect(modal.getAttribute("data-namespace")).toBe("default");
});
});
it('Shell modal receives pod.namespace ("default"), not filter "all"', async () => {
render(
<PodList
pods={[mockPod]}
clusterId="c1"
namespace="all"
onRefresh={() => {}}
/>
);
openActionMenu();
fireEvent.click(screen.getByRole("button", { name: /^shell$/i }));
await waitFor(() => {
const modal = screen.getByTestId("shell-modal");
expect(modal.getAttribute("data-namespace")).toBe("default");
});
});
it('Attach modal receives pod.namespace ("default"), not filter "all"', async () => {
render(
<PodList
pods={[mockPod]}
clusterId="c1"
namespace="all"
onRefresh={() => {}}
/>
);
openActionMenu();
fireEvent.click(screen.getByRole("button", { name: /^attach$/i }));
await waitFor(() => {
const modal = screen.getByTestId("attach-modal");
expect(modal.getAttribute("data-namespace")).toBe("default");
});
});
});