test(data-table): add tests for DataTableSearch

This commit is contained in:
Eric Liu 2025-04-19 12:34:02 -07:00
commit b034378277
2 changed files with 310 additions and 0 deletions

View file

@ -0,0 +1,78 @@
<script lang="ts">
import {
Button,
DataTable,
Toolbar,
ToolbarContent,
ToolbarSearch,
Pagination,
} from "carbon-components-svelte";
import type { ComponentProps } from "svelte";
export let value = "";
export let persistent = false;
export let shouldFilterRows: ComponentProps<ToolbarSearch>["shouldFilterRows"] = true;
const initialRows = Array.from({ length: 10 }).map((_, i) => ({
id: i,
name: "Load Balancer " + (i + 1),
protocol: "HTTP",
port: 3000 + i * 10,
rule: i % 2 ? "Round robin" : "DNS delegation",
}));
let rows = initialRows;
let pageSize = 5;
let page = 1;
let filteredRowIds: number[] = [];
let toggleRows = false;
</script>
<Button
on:click={() => {
toggleRows = !toggleRows;
if (toggleRows) {
rows = Array.from({ length: 4 }).map((_, i) => ({
id: i,
name: "Server instance " + (i + 1),
protocol: "HTTP",
port: 3000 + i * 10,
rule: i % 2 ? "Round!" : "DNS!",
}));
} else {
rows = initialRows;
}
}}
>
Toggle rows
</Button>
<DataTable
headers={[
{ key: "name", value: "Name" },
{ key: "protocol", value: "Protocol" },
{ key: "port", value: "Port" },
{ key: "rule", value: "Rule" },
]}
{rows}
{pageSize}
{page}
>
<Toolbar>
<ToolbarContent>
<ToolbarSearch
{persistent}
{value}
{shouldFilterRows}
bind:filteredRowIds
/>
</ToolbarContent>
</Toolbar>
</DataTable>
<Pagination
bind:pageSize
bind:page
totalItems={filteredRowIds.length}
pageSizeInputDisabled
/>

View file

@ -0,0 +1,232 @@
import { render, screen } from "@testing-library/svelte";
import { user } from "../setup-tests";
import DataTableSearch from "./DataTableSearch.test.svelte";
describe("DataTableSearch", () => {
beforeEach(() => {
vi.clearAllMocks();
});
// Remove first row since it's the header
const getTableRows = () => screen.getAllByRole("row").slice(1);
const getNextPageButton = () =>
screen.getByRole("button", { name: "Next page" });
const getPrevPageButton = () =>
screen.getByRole("button", { name: "Previous page" });
const allRowsRendered = () => {
const tableRows = getTableRows();
expect(tableRows).toHaveLength(5);
tableRows.forEach((row) => {
expect(row).toHaveTextContent(/Round robin|DNS delegation/);
});
expect(screen.getByText("15 of 10 items")).toBeInTheDocument();
expect(screen.getByText("of 2 pages")).toBeInTheDocument();
expect(getNextPageButton()).toBeEnabled();
expect(getPrevPageButton()).toBeDisabled();
};
it("renders non-persistent search input", async () => {
render(DataTableSearch);
const searchBar = screen.getByRole("search");
expect(searchBar).not.toHaveClass(
"bx--toolbar-search-container-persistent",
);
expect(searchBar).not.toHaveClass("bx--toolbar-search-container-active");
const searchInput = screen.getByRole("searchbox");
expect(searchInput).toHaveValue("");
expect(searchInput).not.toHaveFocus();
allRowsRendered();
await user.type(searchInput, "dns");
expect(searchInput).toHaveValue("dns");
expect(searchInput).toHaveFocus();
expect(searchBar).toHaveClass("bx--toolbar-search-container-active");
expect(screen.getByText("15 of 5 items")).toBeInTheDocument();
expect(screen.getByText("of 1 page")).toBeInTheDocument();
expect(getNextPageButton()).toBeDisabled();
expect(getPrevPageButton()).toBeDisabled();
let tableRows = getTableRows();
expect(tableRows).toHaveLength(5);
tableRows.forEach((row) => {
expect(row).toHaveTextContent("DNS");
});
await user.keyboard("{Tab}{Enter}");
expect(searchInput).toHaveValue("");
expect(searchInput).toHaveFocus();
allRowsRendered();
await user.keyboard("{Tab}");
expect(searchBar).not.toHaveClass("bx--toolbar-search-container-active");
});
it("renders persistent search input", async () => {
render(DataTableSearch, {
props: {
persistent: true,
},
});
const searchBar = screen.getByRole("search");
expect(searchBar).toHaveClass("bx--toolbar-search-container-persistent");
const searchInput = screen.getByRole("searchbox");
expect(searchInput).toHaveValue("");
expect(searchInput).not.toHaveFocus();
allRowsRendered();
await user.type(searchInput, "dns");
expect(searchInput).toHaveValue("dns");
expect(searchInput).toHaveFocus();
expect(screen.getByText("15 of 5 items")).toBeInTheDocument();
expect(screen.getByText("of 1 page")).toBeInTheDocument();
expect(getNextPageButton()).toBeDisabled();
expect(getPrevPageButton()).toBeDisabled();
let tableRows = getTableRows();
expect(tableRows).toHaveLength(5);
tableRows.forEach((row) => {
expect(row).toHaveTextContent("DNS");
});
await user.keyboard("{Tab}{Enter}");
expect(searchInput).toHaveValue("");
expect(searchInput).toHaveFocus();
allRowsRendered();
});
it("renders with initial search value in non-persistent search input", async () => {
render(DataTableSearch, {
props: {
value: "round",
},
});
const searchInput = screen.getByRole("searchbox");
expect(searchInput).toHaveValue("round");
expect(searchInput).not.toHaveFocus();
// Search bar should be active.
const searchBar = screen.getByRole("search");
expect(searchBar).toHaveClass("bx--toolbar-search-container-active");
expect(screen.getByText("15 of 5 items")).toBeInTheDocument();
expect(screen.getByText("of 1 page")).toBeInTheDocument();
expect(getNextPageButton()).toBeDisabled();
expect(getPrevPageButton()).toBeDisabled();
let tableRows = getTableRows();
expect(tableRows).toHaveLength(5);
tableRows.forEach((row) => {
expect(row).toHaveTextContent("Round");
});
await user.click(
screen.getByRole("button", { name: "Clear search input" }),
);
expect(searchInput).toHaveValue("");
expect(searchInput).toHaveFocus();
allRowsRendered();
await user.type(searchInput, "rr");
tableRows = getTableRows();
expect(tableRows).toHaveLength(0);
expect(screen.getByText("00 of 0 items")).toBeInTheDocument();
expect(screen.getByText("of 1 page")).toBeInTheDocument();
expect(getNextPageButton()).toBeDisabled();
expect(getPrevPageButton()).toBeDisabled();
await user.keyboard("{Escape}");
expect(searchInput).toHaveValue("");
expect(searchInput).toHaveFocus();
allRowsRendered();
await user.keyboard("{Tab}");
expect(searchBar).not.toHaveClass("bx--toolbar-search-container-active");
});
it("can filter with a custom filter function", async () => {
render(DataTableSearch, {
props: {
shouldFilterRows: (row, value) => {
return (
/(6|8)$/.test(row.name) &&
row.rule.toLowerCase().includes((value + "").toLowerCase())
);
},
},
});
allRowsRendered();
const searchInput = screen.getByRole("searchbox");
await user.type(searchInput, "round");
expect(searchInput).toHaveValue("round");
expect(searchInput).toHaveFocus();
expect(screen.getByText("12 of 2 items")).toBeInTheDocument();
expect(screen.getByText("of 1 page")).toBeInTheDocument();
expect(getNextPageButton()).toBeDisabled();
expect(getPrevPageButton()).toBeDisabled();
let tableRows = getTableRows();
expect(tableRows).toHaveLength(2);
tableRows.forEach((row) => {
expect(row).toHaveTextContent("Round");
expect(row).toHaveTextContent(/Load Balancer 6|Load Balancer 8/);
});
});
// TODO: fix reactivity
it.skip("re-filters rows when toggled", async () => {
render(DataTableSearch);
allRowsRendered();
const searchInput = screen.getByRole("searchbox");
await user.type(searchInput, "round");
expect(searchInput).toHaveValue("round");
expect(searchInput).toHaveFocus();
expect(screen.getByText("15 of 5 items")).toBeInTheDocument();
expect(screen.getByText("of 1 page")).toBeInTheDocument();
expect(getNextPageButton()).toBeDisabled();
expect(getPrevPageButton()).toBeDisabled();
const toggleButton = screen.getByRole("button", { name: "Toggle rows" });
await user.click(toggleButton);
expect(searchInput).toHaveValue("round");
expect(searchInput).not.toHaveFocus();
expect(screen.getByText("12 of 2 items")).toBeInTheDocument();
expect(screen.getByText("of 1 page")).toBeInTheDocument();
expect(getNextPageButton()).toBeDisabled();
expect(getPrevPageButton()).toBeDisabled();
let tableRows = getTableRows();
expect(tableRows).toHaveLength(2);
tableRows.forEach((row) => {
expect(row).toHaveTextContent("Round!");
});
await user.click(toggleButton);
expect(searchInput).toHaveValue("round");
expect(searchInput).not.toHaveFocus();
expect(screen.getByText("15 of 5 items")).toBeInTheDocument();
expect(screen.getByText("of 1 page")).toBeInTheDocument();
expect(getNextPageButton()).toBeDisabled();
expect(getPrevPageButton()).toBeDisabled();
await user.click(
screen.getByRole("button", { name: "Clear search input" }),
);
expect(searchInput).toHaveValue("");
expect(searchInput).toHaveFocus();
allRowsRendered();
});
});