Replace LogsModal with LogStreamPanel in PodList for streaming logs Add smart positioning to ResourceActionMenu to flip when near bottom Fix dark mode text visibility by applying class to html element Fix YAML editor loading race condition Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
187 lines
4.7 KiB
TypeScript
187 lines
4.7 KiB
TypeScript
import { describe, it, expect, vi } from "vitest";
|
|
import { render, screen, waitFor } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { SecretDataModal } from "@/components/Kubernetes/SecretDataModal";
|
|
|
|
describe("SecretDataModal", () => {
|
|
const mockSecretYaml = `apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: test-secret
|
|
namespace: default
|
|
type: Opaque
|
|
data:
|
|
username: YWRtaW4=
|
|
password: cGFzc3dvcmQxMjM=
|
|
token: dGVzdHRva2VuMTIzNDU=
|
|
`;
|
|
|
|
const mockOnOpenChange = vi.fn();
|
|
|
|
it("renders the secret data modal", () => {
|
|
render(
|
|
<SecretDataModal
|
|
open={true}
|
|
onOpenChange={mockOnOpenChange}
|
|
secretName="test-secret"
|
|
secretYaml={mockSecretYaml}
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText(/Secret Data: test-secret/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it("displays secret keys in the table", () => {
|
|
render(
|
|
<SecretDataModal
|
|
open={true}
|
|
onOpenChange={mockOnOpenChange}
|
|
secretName="test-secret"
|
|
secretYaml={mockSecretYaml}
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText("username")).toBeInTheDocument();
|
|
expect(screen.getByText("password")).toBeInTheDocument();
|
|
expect(screen.getByText("token")).toBeInTheDocument();
|
|
});
|
|
|
|
it("initially hides all secret values", () => {
|
|
render(
|
|
<SecretDataModal
|
|
open={true}
|
|
onOpenChange={mockOnOpenChange}
|
|
secretName="test-secret"
|
|
secretYaml={mockSecretYaml}
|
|
/>
|
|
);
|
|
|
|
const cells = screen.getAllByText("••••••••");
|
|
expect(cells.length).toBeGreaterThanOrEqual(3);
|
|
});
|
|
|
|
it("reveals secret value when eye icon is clicked", async () => {
|
|
const user = userEvent.setup();
|
|
|
|
render(
|
|
<SecretDataModal
|
|
open={true}
|
|
onOpenChange={mockOnOpenChange}
|
|
secretName="test-secret"
|
|
secretYaml={mockSecretYaml}
|
|
/>
|
|
);
|
|
|
|
// Find all reveal buttons and click the first one
|
|
const revealButtons = screen.getAllByRole("button", { name: /Reveal value/i });
|
|
await user.click(revealButtons[0]);
|
|
|
|
// Check that the decoded value is now visible
|
|
await waitFor(() => {
|
|
expect(screen.getByText("admin")).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it("hides secret value when eye-off icon is clicked", async () => {
|
|
const user = userEvent.setup();
|
|
|
|
render(
|
|
<SecretDataModal
|
|
open={true}
|
|
onOpenChange={mockOnOpenChange}
|
|
secretName="test-secret"
|
|
secretYaml={mockSecretYaml}
|
|
/>
|
|
);
|
|
|
|
// Reveal first value
|
|
const revealButtons = screen.getAllByRole("button", { name: /Reveal value/i });
|
|
await user.click(revealButtons[0]);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("admin")).toBeInTheDocument();
|
|
});
|
|
|
|
// Hide it again
|
|
const hideButton = screen.getByRole("button", { name: /Hide value/i });
|
|
await user.click(hideButton);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.queryByText("admin")).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it("copies secret value to clipboard when copy icon is clicked", async () => {
|
|
const user = userEvent.setup();
|
|
const mockWriteText = vi.fn().mockResolvedValue(undefined);
|
|
Object.defineProperty(navigator, "clipboard", {
|
|
value: { writeText: mockWriteText },
|
|
writable: true,
|
|
configurable: true,
|
|
});
|
|
|
|
render(
|
|
<SecretDataModal
|
|
open={true}
|
|
onOpenChange={mockOnOpenChange}
|
|
secretName="test-secret"
|
|
secretYaml={mockSecretYaml}
|
|
/>
|
|
);
|
|
|
|
// Find all copy buttons and click the first one
|
|
const copyButtons = screen.getAllByRole("button", { name: /Copy to clipboard/i });
|
|
await user.click(copyButtons[0]);
|
|
|
|
await waitFor(() => {
|
|
expect(mockWriteText).toHaveBeenCalledWith("admin");
|
|
});
|
|
});
|
|
|
|
it("displays empty state when no data keys exist", () => {
|
|
const emptySecretYaml = `apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: empty-secret
|
|
namespace: default
|
|
type: Opaque
|
|
data: {}
|
|
`;
|
|
|
|
render(
|
|
<SecretDataModal
|
|
open={true}
|
|
onOpenChange={mockOnOpenChange}
|
|
secretName="empty-secret"
|
|
secretYaml={emptySecretYaml}
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText("No data keys in this secret.")).toBeInTheDocument();
|
|
});
|
|
|
|
it("handles malformed base64 gracefully", () => {
|
|
const invalidSecretYaml = `apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: invalid-secret
|
|
namespace: default
|
|
type: Opaque
|
|
data:
|
|
invalid: !!!not-base64!!!
|
|
`;
|
|
|
|
render(
|
|
<SecretDataModal
|
|
open={true}
|
|
onOpenChange={mockOnOpenChange}
|
|
secretName="invalid-secret"
|
|
secretYaml={invalidSecretYaml}
|
|
/>
|
|
);
|
|
|
|
// Should still render without crashing
|
|
expect(screen.getByText(/Secret Data: invalid-secret/i)).toBeInTheDocument();
|
|
});
|
|
});
|