test(ui): remove redundant compliance coverage

This commit is contained in:
alejandrobailo
2026-07-22 14:10:34 +02:00
parent aa1bdba22c
commit ce23fa7ffa
11 changed files with 30 additions and 555 deletions
@@ -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");
});
});
@@ -67,21 +67,6 @@ describe("CompliancePageTabs", () => {
expect(pushMock).toHaveBeenCalledWith("/compliance");
});
it("anchors the tour to the scan-mode controls", () => {
const { container } = render(
<CompliancePageTabs
activeTab={COMPLIANCE_TAB.PER_SCAN}
crossProviderEnabled
perScanContent={<div>Per scan content</div>}
crossProviderContent={<div>Cross provider content</div>}
/>,
);
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(
@@ -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(
<RequirementAccountChips
accounts={{
[meta[0].id]: "FAIL",
[meta[1].id]: "FAIL",
[meta[2].id]: "PASS",
[meta[3].id]: "MANUAL",
}}
accountMeta={meta}
/>,
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(<RequirementAccountChips accounts={accounts} accountMeta={meta} />);
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", () => {
@@ -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(<RequirementStatusSummary entries={entries} />);
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();
});
});
@@ -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<string, CrossAccountRequirementExtras>([
[
"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();
});
});
@@ -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();
});
});
@@ -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"],
@@ -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
+1 -13
View File
@@ -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(<Card interactive>Framework</Card>);
expect(screen.getByText("Framework")).toHaveClass(
"cursor-pointer",
"transition-shadow",
"hover:shadow-md",
);
});
});
-14
View File
@@ -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(<Progress aria-label="Score" value={75} variant="warning" />);
expect(
screen.getByRole("progressbar", { name: "Score" }).firstChild,
).toHaveClass("bg-bg-warning");
});
});
@@ -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(<ScrollArea size="md">Content</ScrollArea>);
expect(
screen.getByText("Content").parentElement?.parentElement,
).toHaveClass("h-72");
});
});