Files
prowler/ui/components/attack-paths/graph/nodes/finding-node.test.tsx
T

117 lines
3.7 KiB
TypeScript

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { type NodeProps, Position } from "@xyflow/react";
import { describe, expect, it, vi } from "vitest";
import { FindingNode } from "@/components/attack-paths/graph/nodes/finding-node";
import type { GraphNode } from "@/types/attack-paths";
const hiddenHandlesMock = vi.hoisted(() => vi.fn(() => null));
vi.mock("@/components/attack-paths/graph/nodes/hidden-handles", () => ({
HiddenHandles: hiddenHandlesMock,
}));
const buildFindingNode = (severity: string, title: string): GraphNode => ({
id: `${severity}-finding`,
labels: ["ProwlerFinding"],
properties: { check_title: title, id: `${severity}-finding`, severity },
});
const buildNodeProps = (graphNode: GraphNode): NodeProps =>
({
id: graphNode.id,
type: "finding",
data: { graphNode },
selected: false,
dragging: false,
zIndex: 0,
isConnectable: false,
positionAbsoluteX: 0,
positionAbsoluteY: 0,
}) as unknown as NodeProps;
describe("FindingNode", () => {
it("positions graph handles for horizontal left-to-right edges", () => {
// Given
const props = buildNodeProps(
buildFindingNode("critical", "Root key exposed"),
);
// When
render(<FindingNode {...props} />);
// Then
expect(hiddenHandlesMock).toHaveBeenCalledWith(
expect.objectContaining({
sourcePosition: Position.Right,
sourceStyle: { left: 97, top: 26 },
targetPosition: Position.Left,
targetStyle: { left: 53, top: 26 },
}),
undefined,
);
});
describe("severity visuals", () => {
it("should render the critical finding risk icon with readable text", () => {
// Given
const props = buildNodeProps(
buildFindingNode("critical", "Root key exposed"),
);
// When
render(<FindingNode {...props} />);
// Then
expect(
screen.getByTestId("attack-path-finding-icon-critical"),
).toHaveAccessibleName("Critical finding risk icon");
expect(screen.getByText("Root key exposed")).toBeInTheDocument();
expect(screen.getByText("critical")).toBeInTheDocument();
});
it("should render a distinct medium finding risk icon with readable text", () => {
// Given
const props = buildNodeProps(
buildFindingNode("medium", "Bucket lacks logging"),
);
// When
render(<FindingNode {...props} />);
// Then
expect(
screen.getByTestId("attack-path-finding-icon-medium"),
).toHaveAccessibleName("Medium finding risk icon");
expect(
screen.queryByTestId("attack-path-finding-icon-critical"),
).not.toBeInTheDocument();
expect(screen.getByText("Bucket lacks")).toBeInTheDocument();
expect(screen.getByText("logging")).toBeInTheDocument();
expect(screen.getByText("medium")).toBeInTheDocument();
});
it("should expose the full finding title as an immediate tooltip when truncated", async () => {
// Given
const title =
"Ensure administrator access policies are rotated regularly";
const props = buildNodeProps(buildFindingNode("high", title));
// When
render(<FindingNode {...props} />);
// Then
expect(screen.getByText("Ensure")).toBeInTheDocument();
expect(screen.getByText("administrator")).toBeInTheDocument();
expect(screen.getByText("access policies")).toBeInTheDocument();
expect(screen.getByText("are rotated…")).toBeInTheDocument();
expect(screen.getByText("high")).toBeInTheDocument();
await userEvent.hover(screen.getByTestId("attack-path-finding-node"));
expect(await screen.findAllByText(title)).not.toHaveLength(0);
});
});
});