From ce23fa7ffa2fd5c980a62ddf82e9af8d8a2e2980 Mon Sep 17 00:00:00 2001 From: alejandrobailo Date: Wed, 22 Jul 2026 14:10:34 +0200 Subject: [PATCH] test(ui): remove redundant compliance coverage --- .../compliance/_actions/cross-account.test.ts | 89 +----- .../_components/compliance-page-tabs.test.tsx | 15 - .../requirement-account-chips.test.tsx | 48 +-- .../requirement-status-summary.test.tsx | 30 -- .../cross-account-accordion.test.tsx | 280 ------------------ .../cross-account-mapper-parity.test.tsx | 33 --- .../_lib/__tests__/cross-account-pdf.test.ts | 13 - .../cross-provider-accordion.test.tsx | 35 --- ui/components/shadcn/card/card.test.tsx | 14 +- ui/components/shadcn/progress.test.tsx | 14 - .../shadcn/scroll-area/scroll-area.test.tsx | 14 - 11 files changed, 30 insertions(+), 555 deletions(-) delete mode 100644 ui/app/(prowler)/compliance/_components/requirement-status-summary.test.tsx delete mode 100644 ui/app/(prowler)/compliance/_lib/__tests__/cross-account-accordion.test.tsx delete mode 100644 ui/components/shadcn/progress.test.tsx delete mode 100644 ui/components/shadcn/scroll-area/scroll-area.test.tsx diff --git a/ui/app/(prowler)/compliance/_actions/cross-account.test.ts b/ui/app/(prowler)/compliance/_actions/cross-account.test.ts index f79cc21ea9..e0b8957467 100644 --- a/ui/app/(prowler)/compliance/_actions/cross-account.test.ts +++ b/ui/app/(prowler)/compliance/_actions/cross-account.test.ts @@ -28,12 +28,7 @@ vi.mock("@sentry/nextjs", () => ({ captureException: captureExceptionMock, })); -import { - generateCrossAccountPdf, - getCrossAccountComplianceOverview, - getCrossAccountPdfBinary, - getLatestCrossAccountPdf, -} from "./cross-account"; +import { getCrossAccountComplianceOverview } from "./cross-account"; const jsonResponse = (body: unknown, status = 200) => new Response(JSON.stringify(body), { @@ -41,13 +36,10 @@ const jsonResponse = (body: unknown, status = 200) => headers: { "Content-Type": "application/vnd.api+json" }, }); -const lastFetchCall = () => { +const lastFetchUrl = () => { const call = fetchMock.mock.calls.at(-1); if (!call) throw new Error("fetch was not called"); - return { - init: call[1] as RequestInit, - url: new URL(String(call[0])), - }; + return new URL(String(call[0])); }; beforeEach(() => { @@ -78,7 +70,7 @@ describe("cross-account compliance actions", () => { }); expect(result).toEqual({ status: "success", response: payload }); - const { init, url } = lastFetchCall(); + const url = lastFetchUrl(); expect(url.pathname).toBe("/api/v1/cross-account-compliance-overviews"); expect(url.searchParams.get("filter[compliance_id]")).toBe("cis_2.0_aws"); expect(url.searchParams.get("filter[provider_type]")).toBe("aws"); @@ -87,7 +79,6 @@ describe("cross-account compliance actions", () => { "provider-1,provider-2", ); expect(url.searchParams.get("filter[provider_groups__in]")).toBe("group-1"); - expect(init.signal).toBeInstanceOf(AbortSignal); }); it("aborts a stalled request and reports the network failure", async () => { @@ -117,76 +108,4 @@ describe("cross-account compliance actions", () => { expect(requestSignal?.aborted).toBe(true); expect(captureExceptionMock).toHaveBeenCalledTimes(1); }); - - it("starts PDF generation through the shared task protocol", async () => { - fetchMock.mockResolvedValue( - jsonResponse({ data: { type: "tasks", id: "task-1" } }, 202), - ); - - const result = await generateCrossAccountPdf({ - complianceId: "cis_2.0_aws", - providerType: "aws", - filters: { scanIds: ["scan-1"] }, - reportName: "aws-report.pdf", - }); - - expect(result).toEqual({ taskId: "task-1" }); - const { init, url } = lastFetchCall(); - expect(init.method).toBe("POST"); - expect(init.signal).toBeInstanceOf(AbortSignal); - expect(url.pathname).toBe("/api/v1/cross-account-compliance-overviews/pdf"); - expect(url.searchParams.get("report_name")).toBe("aws-report.pdf"); - }); - - it("retrieves a completed PDF with its response filename", async () => { - fetchMock.mockResolvedValue( - new Response(Buffer.from("pdf-bytes"), { - headers: { - "Content-Disposition": 'attachment; filename="aws-report.pdf"', - "Content-Type": "application/pdf", - }, - }), - ); - - const result = await getCrossAccountPdfBinary("task-1"); - - expect(result).toEqual({ - success: true, - data: Buffer.from("pdf-bytes").toString("base64"), - filename: "aws-report.pdf", - }); - expect(lastFetchCall().init.signal).toBeInstanceOf(AbortSignal); - }); - - it("returns the latest matching PDF descriptor", async () => { - fetchMock.mockResolvedValue( - jsonResponse({ - data: { - id: "task-9", - attributes: { - completed_at: "2026-07-01T10:00:00Z", - result: { filename: "latest.pdf" }, - }, - }, - }), - ); - - const result = await getLatestCrossAccountPdf({ - complianceId: "cis_2.0_aws", - providerType: "aws", - filters: { providerIds: "provider-1" }, - }); - - expect(result).toEqual({ - taskId: "task-9", - filename: "latest.pdf", - completedAt: "2026-07-01T10:00:00Z", - }); - const { init, url } = lastFetchCall(); - expect(init.signal).toBeInstanceOf(AbortSignal); - expect(url.pathname).toBe( - "/api/v1/cross-account-compliance-overviews/pdf/latest", - ); - expect(url.searchParams.get("filter[provider_id__in]")).toBe("provider-1"); - }); }); diff --git a/ui/app/(prowler)/compliance/_components/compliance-page-tabs.test.tsx b/ui/app/(prowler)/compliance/_components/compliance-page-tabs.test.tsx index 0671e43270..80103ec293 100644 --- a/ui/app/(prowler)/compliance/_components/compliance-page-tabs.test.tsx +++ b/ui/app/(prowler)/compliance/_components/compliance-page-tabs.test.tsx @@ -67,21 +67,6 @@ describe("CompliancePageTabs", () => { expect(pushMock).toHaveBeenCalledWith("/compliance"); }); - it("anchors the tour to the scan-mode controls", () => { - const { container } = render( - Per scan content} - crossProviderContent={
Cross provider content
} - />, - ); - - expect( - container.querySelector('[data-tour-id="view-compliance-tabs"]'), - ).toBeInTheDocument(); - }); - it("opens the cross-provider upgrade without changing tabs in Local Server", async () => { const user = userEvent.setup(); render( diff --git a/ui/app/(prowler)/compliance/_components/requirement-account-chips.test.tsx b/ui/app/(prowler)/compliance/_components/requirement-account-chips.test.tsx index 50553d36bc..ea89f1bc17 100644 --- a/ui/app/(prowler)/compliance/_components/requirement-account-chips.test.tsx +++ b/ui/app/(prowler)/compliance/_components/requirement-account-chips.test.tsx @@ -1,7 +1,8 @@ import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; import { describe, expect, it } from "vitest"; -import type { CrossAccountAccountRef } from "../_types"; +import type { AccountStatusMap, CrossAccountAccountRef } from "../_types"; import { RequirementAccountChips } from "./requirement-account-chips"; @@ -28,32 +29,33 @@ describe("RequirementAccountChips", () => { ).not.toBeInTheDocument(); }); - it("collapses to per-status counts beyond two accounts", () => { - const meta = [ - account(1, "prod"), - account(2, "staging"), - account(3, "dev"), - account(4, null), - ]; - render( - , + it("collapses many accounts without hiding the full breakdown", async () => { + const user = userEvent.setup(); + const meta = Array.from({ length: 13 }, (_, index) => + account(index + 1, `account-${index + 1}`), + ); + const accounts: AccountStatusMap = Object.fromEntries( + meta.map((entry, index) => { + const status = index < 2 ? "FAIL" : index === 2 ? "MANUAL" : "PASS"; + return [entry.id, status]; + }), ); - // Constant-footprint summary: one count per status present, no - // per-account labels inline. - const summary = screen.getByTestId("requirement-status-summary"); + render(); + + const summary = screen.getByRole("button", { + name: "Show status breakdown for 13 providers", + }); expect(summary).toHaveTextContent("Fail×2"); expect(summary).toHaveTextContent("Manual×1"); - expect(summary).toHaveTextContent("Pass×1"); - expect(screen.queryByText("prod")).not.toBeInTheDocument(); + expect(summary).toHaveTextContent("Pass×10"); + expect(screen.queryByText("account-1")).not.toBeInTheDocument(); + + await user.click(summary); + + expect(screen.getByText(/^account-1 \(/)).toBeVisible(); + expect(screen.getByText(/^account-13 \(/)).toBeVisible(); + expect(screen.queryByText(/more/)).not.toBeInTheDocument(); }); it("only counts accounts that contributed a status", () => { diff --git a/ui/app/(prowler)/compliance/_components/requirement-status-summary.test.tsx b/ui/app/(prowler)/compliance/_components/requirement-status-summary.test.tsx deleted file mode 100644 index 25acb5a624..0000000000 --- a/ui/app/(prowler)/compliance/_components/requirement-status-summary.test.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { render, screen } from "@testing-library/react"; -import userEvent from "@testing-library/user-event"; -import { describe, expect, it } from "vitest"; - -import { RequirementStatusSummary } from "./requirement-status-summary"; - -describe("RequirementStatusSummary", () => { - it("exposes the complete breakdown from a keyboard-accessible trigger", async () => { - const user = userEvent.setup(); - const entries = Array.from({ length: 13 }, (_, index) => ({ - key: `account-${index}`, - label: `Account ${index + 1}`, - status: index === 0 ? ("FAIL" as const) : ("PASS" as const), - })); - - render(); - - const trigger = screen.getByRole("button", { - name: "Show status breakdown for 13 providers", - }); - trigger.focus(); - expect(trigger).toHaveFocus(); - - await user.click(trigger); - - expect(screen.getByText("Account 1")).toBeVisible(); - expect(screen.getByText("Account 13")).toBeVisible(); - expect(screen.queryByText(/more/)).not.toBeInTheDocument(); - }); -}); diff --git a/ui/app/(prowler)/compliance/_lib/__tests__/cross-account-accordion.test.tsx b/ui/app/(prowler)/compliance/_lib/__tests__/cross-account-accordion.test.tsx deleted file mode 100644 index bbcb99493f..0000000000 --- a/ui/app/(prowler)/compliance/_lib/__tests__/cross-account-accordion.test.tsx +++ /dev/null @@ -1,280 +0,0 @@ -import { render, screen } from "@testing-library/react"; -import { describe, expect, it, vi } from "vitest"; - -import type { Framework } from "@/types/compliance"; - -// Same stubs as the cross-provider accordion test: the requirement content -// drags the findings/server-action chain into jsdom, and the section header -// reaches next-auth through the shadcn/table barrel. Assembly structure is -// what's under test here. -vi.mock( - "@/components/compliance/compliance-accordion/client-accordion-content", - () => ({ ClientAccordionContent: () => null }), -); -vi.mock( - "@/components/compliance/compliance-accordion/compliance-accordion-title", - () => ({ ComplianceAccordionTitle: () => null }), -); - -import type { - CrossAccountAccountRef, - CrossAccountRequirementExtras, -} from "../../_types"; -import { toCrossAccountAccordionItems } from "../cross-account-accordion"; - -const ACC1 = "11111111-1111-4111-8111-111111111111"; -const ACC2 = "22222222-2222-4222-8222-222222222222"; - -const accountMeta: CrossAccountAccountRef[] = [ - { id: ACC1, uid: "123456789012", alias: "prod" }, - { id: ACC2, uid: "210987654321", alias: null }, -]; - -const data: Framework[] = [ - { - name: "CIS", - pass: 1, - fail: 1, - manual: 0, - categories: [ - { - name: "1. IAM", - pass: 1, - fail: 1, - manual: 0, - controls: [ - { - label: "1. IAM", - pass: 1, - fail: 1, - manual: 0, - requirements: [ - { - name: "1.1 - Maintain contact details", - description: "desc", - status: "FAIL", - pass: 0, - fail: 1, - manual: 0, - check_ids: ["check_a"], - }, - { - name: "1.2 - Security contact", - description: "desc", - status: "PASS", - pass: 1, - fail: 0, - manual: 0, - check_ids: [], - }, - ], - }, - ], - }, - ], - }, -]; - -const extras = new Map([ - [ - "1.1 - Maintain contact details", - { - requirementId: "1.1", - accounts: { [ACC1]: "FAIL", [ACC2]: "PASS" }, - checkIds: ["check_a"], - scanIdsByAccount: { [ACC1]: ["scan-1"], [ACC2]: ["scan-2"] }, - }, - ], -]); - -describe("toCrossAccountAccordionItems", () => { - const items = toCrossAccountAccordionItems(data, extras, "CIS", accountMeta); - - it("keeps the per-scan accordion key scheme so ?section= deep links work", () => { - expect(items).toHaveLength(1); - expect(items[0].key).toBe("CIS-1. IAM"); - expect(items[0].items).toHaveLength(2); - }); - - it("generates unique requirement keys across controls of one category", () => { - // Two controls whose requirement lists both start at index 0 — keying - // on the requirement index alone would collide (React duplicate-key - // warning seen with CIS categories holding several controls). - const twoControls: Framework[] = [ - { - ...data[0], - categories: [ - { - ...data[0].categories[0], - controls: [ - data[0].categories[0].controls[0], - { - ...data[0].categories[0].controls[0], - label: "another control", - }, - ], - }, - ], - }, - ]; - - const keys = toCrossAccountAccordionItems( - twoControls, - extras, - "CIS", - accountMeta, - )[0].items!.map((item) => item.key); - - expect(new Set(keys).size).toBe(keys.length); - }); - - it("shows one labeled chip per contributing account", () => { - const { unmount } = render(<>{items[0].items?.[0].title}); - - expect( - screen.getByText("1.1 - Maintain contact details"), - ).toBeInTheDocument(); - // Both accounts contribute: alias when set, uid otherwise. - expect(screen.getByText("prod")).toBeInTheDocument(); - expect(screen.getByText("210987654321")).toBeInTheDocument(); - expect(screen.getAllByText(/^fail$/i)).toHaveLength(1); - expect(screen.getAllByText(/^pass$/i)).toHaveLength(1); - unmount(); - }); - - it("falls back to a single roll-up badge when a requirement has no per-account breakdown", () => { - render(<>{items[0].items?.[1].title}); - - expect(screen.getByText("1.2 - Security contact")).toBeInTheDocument(); - expect(screen.getAllByText(/^pass$/i)).toHaveLength(1); - }); - - it("uses the control label as the row title for CIS-style 1:1 controls", () => { - // The CIS mapper names requirements with the bare id and keeps the rich - // "id - description" on its one-requirement control — the row must show - // the rich label, matching the Single Scan view. - const cisStyle: Framework[] = [ - { - ...data[0], - categories: [ - { - ...data[0].categories[0], - controls: [ - { - label: "2.1.1 - Ensure centralized root access", - pass: 0, - fail: 1, - manual: 0, - requirements: [ - { - ...data[0].categories[0].controls[0].requirements[0], - name: "2.1.1", - }, - ], - }, - ], - }, - ], - }, - ]; - - const cisItems = toCrossAccountAccordionItems( - cisStyle, - new Map(), - "CIS", - accountMeta, - ); - const { unmount } = render(<>{cisItems[0].items?.[0].title}); - - expect( - screen.getByText("2.1.1 - Ensure centralized root access"), - ).toBeInTheDocument(); - unmount(); - }); - - it("keeps labeled multi-requirement controls as a nested level (ENS style)", () => { - const ensStyle: Framework[] = [ - { - ...data[0], - categories: [ - { - ...data[0].categories[0], - controls: [ - { - ...data[0].categories[0].controls[0], - label: "op.acc - Access control group", - }, - ], - }, - ], - }, - ]; - - const ensItems = toCrossAccountAccordionItems( - ensStyle, - extras, - "ENS", - accountMeta, - ); - - // One nested control item wrapping its requirement rows. - expect(ensItems[0].items).toHaveLength(1); - expect(ensItems[0].items?.[0].items).toHaveLength(2); - }); - - it("keeps multi-framework data (ENS marcos) as the top accordion level", () => { - const marcos: Framework[] = [ - { ...data[0], name: "Operacional" }, - { ...data[0], name: "Organizativo" }, - ]; - - const marcoItems = toCrossAccountAccordionItems( - marcos, - extras, - "ENS", - accountMeta, - ); - - expect(marcoItems.map((item) => item.key)).toEqual([ - "Operacional", - "Organizativo", - ]); - // Categories nest under their marco, keeping the per-scan key scheme. - expect(marcoItems[0].items?.[0].key).toBe("Operacional-1. IAM"); - }); - - it("shows the requirement type chip (requisito/recomendación) like per-scan", () => { - const typed: Framework[] = [ - { - ...data[0], - categories: [ - { - ...data[0].categories[0], - controls: [ - { - ...data[0].categories[0].controls[0], - requirements: [ - { - ...data[0].categories[0].controls[0].requirements[0], - type: "requisito", - }, - ], - }, - ], - }, - ], - }, - ]; - - const typedItems = toCrossAccountAccordionItems( - typed, - extras, - "ENS", - accountMeta, - ); - const { unmount } = render(<>{typedItems[0].items?.[0].title}); - - expect(screen.getByText("requisito")).toBeInTheDocument(); - unmount(); - }); -}); diff --git a/ui/app/(prowler)/compliance/_lib/__tests__/cross-account-mapper-parity.test.tsx b/ui/app/(prowler)/compliance/_lib/__tests__/cross-account-mapper-parity.test.tsx index 704fd36066..95aa963e80 100644 --- a/ui/app/(prowler)/compliance/_lib/__tests__/cross-account-mapper-parity.test.tsx +++ b/ui/app/(prowler)/compliance/_lib/__tests__/cross-account-mapper-parity.test.tsx @@ -309,37 +309,4 @@ describe("cross-account pipeline parity with real mappers", () => { crossAccountLeaves.map((t) => t.replace(/Fail$/, "")).sort(), ).toEqual(perScanLeaves.sort()); }); - - it("generic (onboarding-style): rows show the requirement name", () => { - const attrs = buildAttrs( - [ - { - id: "Predefine IAM Roles", - name: "Predefine IAM Roles", - description: "Predefine IAM roles for the account", - attributes: [ - { Section: "Deploy account from predefined IaC template" }, - ], - status: "FAIL", - accounts: { [ACC]: "FAIL" }, - check_ids: ["check_a"], - }, - ], - "AWS-Account-Security-Onboarding", - ); - - const { attributesData, requirementsData } = - crossAccountToMapperInput(attrs); - const data = mapGeneric(attributesData, requirementsData); - const items = toCrossAccountAccordionItems( - data, - new Map(), - "AWS-Account-Security-Onboarding", - accountMeta, - ); - - const { unmount } = render(<>{items[0].items?.[0].title}); - expect(screen.getByText("Predefine IAM Roles")).toBeInTheDocument(); - unmount(); - }); }); diff --git a/ui/app/(prowler)/compliance/_lib/__tests__/cross-account-pdf.test.ts b/ui/app/(prowler)/compliance/_lib/__tests__/cross-account-pdf.test.ts index 47129e8c0c..156ed50a4a 100644 --- a/ui/app/(prowler)/compliance/_lib/__tests__/cross-account-pdf.test.ts +++ b/ui/app/(prowler)/compliance/_lib/__tests__/cross-account-pdf.test.ts @@ -16,19 +16,6 @@ vi.mock("@/lib/helper", () => ({ import { buildCrossAccountPdfTaskScope } from "../cross-account-pdf"; describe("buildCrossAccountPdfTaskScope", () => { - it("normalizes set-like filter ordering into one task scope", () => { - const first = buildCrossAccountPdfTaskScope("cis_2.0_aws", "aws", { - scanIds: ["scan-2", "scan-1"], - providerIds: "provider-2,provider-1", - }); - const second = buildCrossAccountPdfTaskScope("cis_2.0_aws", "aws", { - scanIds: ["scan-1", "scan-2"], - providerIds: "provider-1,provider-2", - }); - - expect(first).toBe(second); - }); - it("keeps reports from different provider types in separate scopes", () => { const awsScope = buildCrossAccountPdfTaskScope("cis_2.0_aws", "aws", { scanIds: ["scan-1"], diff --git a/ui/app/(prowler)/compliance/_lib/__tests__/cross-provider-accordion.test.tsx b/ui/app/(prowler)/compliance/_lib/__tests__/cross-provider-accordion.test.tsx index 72d8c4ab96..60022241f0 100644 --- a/ui/app/(prowler)/compliance/_lib/__tests__/cross-provider-accordion.test.tsx +++ b/ui/app/(prowler)/compliance/_lib/__tests__/cross-provider-accordion.test.tsx @@ -87,41 +87,6 @@ describe("toCrossProviderAccordionItems", () => { expect(items[0].items).toHaveLength(2); }); - it("uses the control label as the row title for CIS-style 1:1 controls", () => { - const cisStyle: Framework[] = [ - { - ...data[0], - categories: [ - { - ...data[0].categories[0], - controls: [ - { - label: "2.1.1 - Ensure centralized root access", - pass: 0, - fail: 1, - manual: 0, - requirements: [ - { - ...data[0].categories[0].controls[0].requirements[0], - name: "2.1.1", - }, - ], - }, - ], - }, - ], - }, - ]; - - const cisItems = toCrossProviderAccordionItems(cisStyle, new Map(), "CIS"); - const { unmount } = render(<>{cisItems[0].items?.[0].title}); - - expect( - screen.getByText("2.1.1 - Ensure centralized root access"), - ).toBeInTheDocument(); - unmount(); - }); - it("generates unique requirement keys across controls of one category", () => { // Two controls whose requirement lists both start at index 0 — keying // on the requirement index alone would collide (React duplicate-key diff --git a/ui/components/shadcn/card/card.test.tsx b/ui/components/shadcn/card/card.test.tsx index 37fa8d3508..8d2e1ec7fa 100644 --- a/ui/components/shadcn/card/card.test.tsx +++ b/ui/components/shadcn/card/card.test.tsx @@ -1,7 +1,7 @@ import { render, screen } from "@testing-library/react"; import { describe, expect, it } from "vitest"; -import { Card, CardHeader } from "./card"; +import { CardHeader } from "./card"; describe("CardHeader", () => { it("does not add vertical margin by default", () => { @@ -15,15 +15,3 @@ describe("CardHeader", () => { expect(header).not.toHaveClass("mb-6"); }); }); - -describe("Card", () => { - it("provides the shared interactive treatment without call-site classes", () => { - render(Framework); - - expect(screen.getByText("Framework")).toHaveClass( - "cursor-pointer", - "transition-shadow", - "hover:shadow-md", - ); - }); -}); diff --git a/ui/components/shadcn/progress.test.tsx b/ui/components/shadcn/progress.test.tsx deleted file mode 100644 index a1687ad313..0000000000 --- a/ui/components/shadcn/progress.test.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { render, screen } from "@testing-library/react"; -import { describe, expect, it } from "vitest"; - -import { Progress } from "./progress"; - -describe("Progress", () => { - it("provides semantic indicator variants", () => { - render(); - - expect( - screen.getByRole("progressbar", { name: "Score" }).firstChild, - ).toHaveClass("bg-bg-warning"); - }); -}); diff --git a/ui/components/shadcn/scroll-area/scroll-area.test.tsx b/ui/components/shadcn/scroll-area/scroll-area.test.tsx deleted file mode 100644 index c076c60128..0000000000 --- a/ui/components/shadcn/scroll-area/scroll-area.test.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { render, screen } from "@testing-library/react"; -import { describe, expect, it } from "vitest"; - -import { ScrollArea } from "./scroll-area"; - -describe("ScrollArea", () => { - it("provides a medium viewport height", () => { - render(Content); - - expect( - screen.getByText("Content").parentElement?.parentElement, - ).toHaveClass("h-72"); - }); -});