mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 12:31:54 +00:00
test(ui): remove redundant compliance coverage
This commit is contained in:
@@ -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,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",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user