mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-11-08 20:33:41 +00:00
234 lines
6.7 KiB
TypeScript
234 lines
6.7 KiB
TypeScript
import { render, screen } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import type { MetaDataProps } from "@/types";
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
searchParams: new URLSearchParams(),
|
|
push: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("next/navigation", () => ({
|
|
usePathname: () => "/providers",
|
|
useRouter: () => ({ push: mocks.push }),
|
|
useSearchParams: () => mocks.searchParams,
|
|
}));
|
|
|
|
vi.mock("@/lib", () => ({
|
|
getPaginationInfo: (metadata: MetaDataProps) => ({
|
|
currentPage: metadata.pagination.page,
|
|
totalPages: metadata.pagination.pages,
|
|
totalEntries: metadata.pagination.count,
|
|
itemsPerPageOptions: metadata.pagination.itemsPerPage ?? [10, 20, 50],
|
|
}),
|
|
}));
|
|
|
|
vi.mock("@/components/shadcn/select/select", () => ({
|
|
// data-value stands in for the trigger label
|
|
Select: ({
|
|
value,
|
|
children,
|
|
}: {
|
|
value: string;
|
|
children: React.ReactNode;
|
|
}) => (
|
|
<div data-testid="page-size-select" data-value={value}>
|
|
{children}
|
|
</div>
|
|
),
|
|
SelectContent: ({ children }: { children: React.ReactNode }) => (
|
|
<div>{children}</div>
|
|
),
|
|
SelectItem: ({
|
|
children,
|
|
value,
|
|
}: {
|
|
children: React.ReactNode;
|
|
value: string;
|
|
}) => <option value={value}>{children}</option>,
|
|
SelectTrigger: ({ children }: { children: React.ReactNode }) => (
|
|
<button type="button">{children}</button>
|
|
),
|
|
SelectValue: () => null,
|
|
}));
|
|
|
|
import { DataTablePagination } from "./data-table-pagination";
|
|
|
|
const metadata: MetaDataProps = {
|
|
pagination: {
|
|
page: 2,
|
|
pages: 4,
|
|
count: 40,
|
|
itemsPerPage: [10, 20, 50],
|
|
},
|
|
version: "latest",
|
|
};
|
|
|
|
describe("DataTablePagination", () => {
|
|
beforeEach(() => {
|
|
mocks.searchParams = new URLSearchParams();
|
|
});
|
|
|
|
it("keeps navigation arrows visible on hover in light theme", () => {
|
|
render(<DataTablePagination metadata={metadata} />);
|
|
|
|
expect(screen.getByLabelText("Go to first page")).toHaveClass(
|
|
"hover:text-text-neutral-primary",
|
|
);
|
|
expect(screen.getByLabelText("Go to first page")).toHaveClass(
|
|
"hover:bg-bg-neutral-tertiary",
|
|
);
|
|
expect(screen.getByLabelText("Go to next page")).toHaveClass(
|
|
"hover:text-text-neutral-primary",
|
|
);
|
|
});
|
|
|
|
it("does not render an empty pagination container when there is only one page", () => {
|
|
// Given - Metadata for a table that does not need pagination
|
|
const singlePageMetadata: MetaDataProps = {
|
|
pagination: {
|
|
page: 1,
|
|
pages: 1,
|
|
count: 1,
|
|
itemsPerPage: [10, 20, 50],
|
|
},
|
|
version: "latest",
|
|
};
|
|
|
|
// When - Rendering pagination
|
|
const { container } = render(
|
|
<DataTablePagination metadata={singlePageMetadata} />,
|
|
);
|
|
|
|
// Then - No wrapper remains to add extra DataTable gap
|
|
expect(container).toBeEmptyDOMElement();
|
|
});
|
|
|
|
it("keeps the rows-per-page selector when the chosen page size collapses the table to a single page", () => {
|
|
const collapsedByPageSize: MetaDataProps = {
|
|
pagination: {
|
|
page: 1,
|
|
pages: 1,
|
|
count: 85,
|
|
itemsPerPage: [10, 20, 30, 50, 100],
|
|
},
|
|
version: "latest",
|
|
};
|
|
mocks.searchParams = new URLSearchParams("pageSize=100");
|
|
|
|
render(<DataTablePagination metadata={collapsedByPageSize} />);
|
|
|
|
expect(screen.getByText("Rows per page")).toBeInTheDocument();
|
|
expect(screen.getByTestId("page-size-select")).toHaveAttribute(
|
|
"data-value",
|
|
"100",
|
|
);
|
|
expect(screen.queryByLabelText("Go to next page")).not.toBeInTheDocument();
|
|
expect(screen.queryByText(/^Page \d+ of \d+$/)).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("keeps the selector when the smallest configured page size would still paginate", () => {
|
|
const smallestOptionPaginates: MetaDataProps = {
|
|
pagination: {
|
|
page: 1,
|
|
pages: 1,
|
|
count: 7,
|
|
itemsPerPage: [5, 10, 25],
|
|
},
|
|
version: "latest",
|
|
};
|
|
|
|
render(<DataTablePagination metadata={smallestOptionPaginates} />);
|
|
|
|
expect(screen.getByText("Rows per page")).toBeInTheDocument();
|
|
});
|
|
|
|
it("hides the selector when no page size option would split the table", () => {
|
|
const nothingToPaginate: MetaDataProps = {
|
|
pagination: {
|
|
page: 1,
|
|
pages: 1,
|
|
count: 7,
|
|
itemsPerPage: [10, 20, 50],
|
|
},
|
|
version: "latest",
|
|
};
|
|
|
|
const { container } = render(
|
|
<DataTablePagination metadata={nothingToPaginate} />,
|
|
);
|
|
|
|
expect(container).toBeEmptyDOMElement();
|
|
});
|
|
|
|
it("follows the page size in the URL when it changes under the mounted selector", () => {
|
|
mocks.searchParams = new URLSearchParams("pageSize=100");
|
|
const { rerender } = render(<DataTablePagination metadata={metadata} />);
|
|
|
|
expect(screen.getByTestId("page-size-select")).toHaveAttribute(
|
|
"data-value",
|
|
"100",
|
|
);
|
|
|
|
// URL changes without remounting: back/forward, filter reset
|
|
mocks.searchParams = new URLSearchParams("pageSize=20");
|
|
rerender(<DataTablePagination metadata={metadata} />);
|
|
|
|
expect(screen.getByTestId("page-size-select")).toHaveAttribute(
|
|
"data-value",
|
|
"20",
|
|
);
|
|
});
|
|
|
|
describe("when the table is locked to a preview", () => {
|
|
const lockedMetadata: MetaDataProps = {
|
|
pagination: { page: 1, pages: 2, count: 25, itemsPerPage: [10, 20, 50] },
|
|
version: "latest",
|
|
};
|
|
|
|
it("counts pages by the visible rows and routes navigation to the lock handler", async () => {
|
|
// Given
|
|
const user = userEvent.setup();
|
|
const onLockedInteraction = vi.fn();
|
|
render(
|
|
<DataTablePagination
|
|
metadata={lockedMetadata}
|
|
lock={{ visibleRowCount: 10, onLockedInteraction }}
|
|
/>,
|
|
);
|
|
|
|
// When
|
|
await user.click(screen.getByLabelText("Go to next page"));
|
|
await user.click(screen.getByLabelText("Go to last page"));
|
|
|
|
// Then
|
|
expect(screen.getByText("Page 1 of 3")).toBeInTheDocument();
|
|
expect(onLockedInteraction).toHaveBeenCalledTimes(2);
|
|
expect(mocks.push).not.toHaveBeenCalled();
|
|
expect(screen.getByLabelText("Go to next page")).not.toHaveAttribute(
|
|
"href",
|
|
);
|
|
});
|
|
|
|
it("opens the lock handler instead of the rows-per-page menu", async () => {
|
|
// Given
|
|
const user = userEvent.setup();
|
|
const onLockedInteraction = vi.fn();
|
|
render(
|
|
<DataTablePagination
|
|
metadata={lockedMetadata}
|
|
lock={{ visibleRowCount: 10, onLockedInteraction }}
|
|
/>,
|
|
);
|
|
|
|
// When
|
|
await user.click(screen.getByRole("button", { name: "10" }));
|
|
|
|
// Then
|
|
expect(onLockedInteraction).toHaveBeenCalledTimes(1);
|
|
expect(screen.queryByTestId("page-size-select")).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|