Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions app/(chat)/findings/__tests__/page.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import "@testing-library/jest-dom";
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";

const mockUsePaginatedQuery = jest.fn();
const mockUseQuery = jest.fn((_ref: unknown, args: any) =>
args?.findingId ? mockFinding : mockSourceChats,
);
const mockCapture = jest.fn();
let mockMobile = false;

jest.mock("convex/react", () => ({
useConvexAuth: () => ({ isLoading: false, isAuthenticated: true }),
usePaginatedQuery: (...args: unknown[]) => mockUsePaginatedQuery(...args),
useQuery: (...args: unknown[]) => mockUseQuery(...args),
useMutation: () => jest.fn(),
}));

jest.mock("@/app/contexts/GlobalState", () => ({
useGlobalState: () => ({
setChatSidebarOpen: jest.fn(),
closeSidebar: jest.fn(),
}),
}));

jest.mock("@/hooks/use-mobile", () => ({
useIsMobile: () => mockMobile,
}));

jest.mock("@/app/hooks/useTauri", () => ({ navigateToAuth: jest.fn() }));
jest.mock("@/lib/analytics/client", () => ({
captureAuthenticatedEvent: mockCapture,
}));

const mockFinding = {
finding_id: "finding-1",
title: "Confirmed IDOR",
target: "https://app.example.test",
endpoint: "/api/invoices/other",
method: "GET",
severity: "high",
cvss_score: 7.1,
chat_id: "chat-1",
chat_title: "Invoice test",
created_at: Date.now(),
updated_at: Date.now(),
message_id: "message-1",
description: "Another account's invoice is readable.",
impact: "Billing data disclosure.",
technical_analysis: "Missing owner predicate.",
poc_description: "Request another account's invoice.",
poc_script_code: "curl /api/invoices/other",
remediation_steps: "Add an owner predicate.",
evidence: "HTTP 200 returned another account's data.",
assumptions: "Ordinary account.",
fix_effort: "low",
cvss_vector: "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
cvss_breakdown: {
attack_vector: "N",
attack_complexity: "L",
privileges_required: "L",
user_interaction: "N",
scope: "U",
confidentiality: "H",
integrity: "N",
availability: "N",
},
};

const mockSourceChats = [{ chat_id: "chat-1", chat_title: "Invoice test" }];

const Page = require("../page").default as typeof import("../page").default;

describe("FindingsPage", () => {
beforeEach(() => {
jest.clearAllMocks();
mockMobile = false;
mockUsePaginatedQuery.mockReturnValue({
results: [mockFinding],
status: "Exhausted",
loadMore: jest.fn(),
});
});

it("lists metadata, searches, and exposes severity/source filters", async () => {
render(<Page />);
expect(screen.getByRole("heading", { name: "Findings" })).toBeVisible();
expect(screen.getByText("Confirmed IDOR")).toBeVisible();
expect(screen.getByText("/api/invoices/other")).toBeVisible();
expect(screen.getByText("Invoice test")).toBeVisible();
expect(screen.getByText("CVSS 7.1")).toBeVisible();
expect(screen.getByLabelText("Filter by severity")).toBeVisible();
expect(screen.getByLabelText("Filter by source chat")).toBeVisible();

fireEvent.change(screen.getByLabelText("Search findings"), {
target: { value: "CWE-639" },
});
await waitFor(() => {
expect(mockUsePaginatedQuery).toHaveBeenLastCalledWith(
expect.anything(),
expect.objectContaining({ search: "CWE-639" }),
{ initialNumItems: 25 },
);
});
expect(mockCapture).toHaveBeenCalledWith("findings_page_viewed");
});

it("opens the reusable detail alongside the desktop list", () => {
render(<Page />);
fireEvent.click(screen.getByText("Confirmed IDOR"));
expect(screen.getByText(mockFinding.description)).toBeVisible();
expect(screen.getByRole("link", { name: /Invoice test/i })).toHaveAttribute(
"href",
"/c/chat-1",
);
expect(mockCapture).toHaveBeenCalledWith("finding_viewed", {
surface: "findings_page",
});
});

it("uses a full-screen mobile detail with a back control", () => {
mockMobile = true;
render(<Page />);
fireEvent.click(screen.getByText("Confirmed IDOR"));
expect(
screen.getByRole("button", { name: "Back to findings" }),
).toBeVisible();
expect(screen.getByText(mockFinding.description)).toBeVisible();
});
});
Loading