mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-14 18:01:06 +00:00
test(data-table): add unit tests (#2144)
This commit is contained in:
parent
c57c0efb73
commit
da2a308d31
9 changed files with 1150 additions and 452 deletions
775
tests/DataTable/DataTable.test.ts
Normal file
775
tests/DataTable/DataTable.test.ts
Normal file
|
@ -0,0 +1,775 @@
|
|||
import { render, screen } from "@testing-library/svelte";
|
||||
import { tick } from "svelte";
|
||||
import { user } from "../setup-tests";
|
||||
import DataTable from "./DataTable.test.svelte";
|
||||
|
||||
describe("DataTable", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
const headers = [
|
||||
{ key: "name", value: "Name" },
|
||||
{ key: "protocol", value: "Protocol" },
|
||||
{ key: "port", value: "Port" },
|
||||
{ key: "rule", value: "Rule" },
|
||||
] as const;
|
||||
|
||||
const rows = [
|
||||
{
|
||||
id: "a",
|
||||
name: "Load Balancer 3",
|
||||
protocol: "HTTP",
|
||||
port: 3000,
|
||||
rule: "Round robin",
|
||||
},
|
||||
{
|
||||
id: "b",
|
||||
name: "Load Balancer 1",
|
||||
protocol: "HTTP",
|
||||
port: 443,
|
||||
rule: "Round robin",
|
||||
},
|
||||
{
|
||||
id: "c",
|
||||
name: "Load Balancer 2",
|
||||
protocol: "HTTP",
|
||||
port: 80,
|
||||
rule: "DNS delegation",
|
||||
},
|
||||
];
|
||||
|
||||
// Basic rendering and structure tests
|
||||
it("renders with default props", async () => {
|
||||
const { container } = render(DataTable);
|
||||
// Check if table headers are rendered
|
||||
headers.forEach((header) => {
|
||||
expect(screen.getByText(header.value)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Check if table has correct structure
|
||||
const table = container.querySelector("table");
|
||||
expect(table).toBeInTheDocument();
|
||||
expect(table).toHaveClass("bx--data-table");
|
||||
|
||||
// Check if table has correct number of rows
|
||||
const tableRows = container.querySelectorAll("tbody tr");
|
||||
expect(tableRows).toHaveLength(3);
|
||||
|
||||
// Check if all rows contain the expected data
|
||||
rows.forEach((row) => {
|
||||
const rowElement = screen.getByText(row.name).closest("tr");
|
||||
expect(rowElement).toBeInTheDocument();
|
||||
|
||||
if (rowElement) {
|
||||
const cells = rowElement.querySelectorAll("td");
|
||||
expect(cells.length).toBe(4);
|
||||
|
||||
const protocolCell = Array.from(cells).find(
|
||||
(cell) => cell.textContent?.trim() === row.protocol,
|
||||
);
|
||||
expect(protocolCell).toBeInTheDocument();
|
||||
|
||||
const portCell = Array.from(cells).find(
|
||||
(cell) => cell.textContent?.trim() === row.port.toString(),
|
||||
);
|
||||
expect(portCell).toBeInTheDocument();
|
||||
|
||||
const ruleCell = Array.from(cells).find(
|
||||
(cell) => cell.textContent?.trim() === row.rule,
|
||||
);
|
||||
expect(ruleCell).toBeInTheDocument();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("renders with title and description", () => {
|
||||
render(DataTable, {
|
||||
props: {
|
||||
title: "Test Table",
|
||||
description: "Test Description",
|
||||
headers,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.getByRole("heading", { name: "Test Table" }),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText("Test Description")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("handles empty table state", () => {
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
headers,
|
||||
rows: [],
|
||||
},
|
||||
});
|
||||
|
||||
const tableBody = container.querySelector("tbody");
|
||||
assert(tableBody);
|
||||
expect(tableBody.children.length).toBe(0);
|
||||
});
|
||||
|
||||
it("handles table with only one column", () => {
|
||||
const singleColumnHeaders = [{ key: "name", value: "Name" }];
|
||||
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
headers: singleColumnHeaders,
|
||||
rows: rows.map(({ id, name }) => ({ id, name })),
|
||||
},
|
||||
});
|
||||
|
||||
const columns = container.querySelectorAll("th");
|
||||
expect(columns.length).toBe(1);
|
||||
expect(columns[0]).toHaveTextContent("Name");
|
||||
});
|
||||
|
||||
it("handles table with very long content", () => {
|
||||
const longContentRows = [
|
||||
{
|
||||
id: "a",
|
||||
name: "A very long name that should be truncated or wrapped in the table cell",
|
||||
protocol: "HTTP",
|
||||
port: 3000,
|
||||
rule: "Round robin",
|
||||
},
|
||||
];
|
||||
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
headers,
|
||||
rows: longContentRows,
|
||||
},
|
||||
});
|
||||
|
||||
const longCell = container.querySelector("td");
|
||||
assert(longCell);
|
||||
expect(longCell).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Sorting tests
|
||||
it("handles sorting functionality", async () => {
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
sortable: true,
|
||||
headers,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
// Get all header cells
|
||||
const headerCells = container.querySelectorAll("th");
|
||||
expect(headerCells.length).toBe(4);
|
||||
|
||||
// Test sorting by name (ascending)
|
||||
const nameHeader = screen.getByText("Name");
|
||||
await user.click(nameHeader);
|
||||
|
||||
// Verify rows are sorted by name ascending
|
||||
const rowsAfterNameSort = container.querySelectorAll("tbody tr");
|
||||
const firstRowName = rowsAfterNameSort[0].querySelector("td");
|
||||
expect(firstRowName).toHaveTextContent("Load Balancer 1");
|
||||
|
||||
// Test sorting by name (descending)
|
||||
await user.click(nameHeader);
|
||||
const rowsAfterNameDescSort = container.querySelectorAll("tbody tr");
|
||||
const firstRowNameDesc = rowsAfterNameDescSort[0].querySelector("td");
|
||||
expect(firstRowNameDesc).toHaveTextContent("Load Balancer 3");
|
||||
|
||||
// Test sorting by port (ascending)
|
||||
const portHeader = screen.getByText("Port");
|
||||
await user.click(portHeader);
|
||||
|
||||
// Verify rows are sorted by port ascending
|
||||
const rowsAfterPortSort = container.querySelectorAll("tbody tr");
|
||||
const firstRowPort = rowsAfterPortSort[0].querySelectorAll("td")[2];
|
||||
expect(firstRowPort).toHaveTextContent("80");
|
||||
|
||||
// Test sorting by port (descending)
|
||||
await user.click(portHeader);
|
||||
const rowsAfterPortDescSort = container.querySelectorAll("tbody tr");
|
||||
const firstRowPortDesc = rowsAfterPortDescSort[0].querySelectorAll("td")[2];
|
||||
expect(firstRowPortDesc).toHaveTextContent("3000");
|
||||
});
|
||||
|
||||
it("handles sorting with custom display and sort methods", async () => {
|
||||
const customHeaders = [
|
||||
{ key: "name", value: "Name" },
|
||||
{ key: "cost", value: "Cost", display: (cost: number) => cost + " €" },
|
||||
{
|
||||
key: "expireDate",
|
||||
value: "Expire date",
|
||||
display: (date: string) => new Date(date).toLocaleString(),
|
||||
sort: (a: string, b: string) =>
|
||||
new Date(a).getTime() - new Date(b).getTime(),
|
||||
},
|
||||
];
|
||||
|
||||
const customRows = [
|
||||
{
|
||||
id: "a",
|
||||
name: "Load Balancer 1",
|
||||
cost: 100,
|
||||
expireDate: "2024-01-01",
|
||||
},
|
||||
{
|
||||
id: "b",
|
||||
name: "Load Balancer 2",
|
||||
cost: 200,
|
||||
expireDate: "2023-12-31",
|
||||
},
|
||||
{
|
||||
id: "c",
|
||||
name: "Load Balancer 3",
|
||||
cost: 150,
|
||||
expireDate: "2024-02-01",
|
||||
},
|
||||
];
|
||||
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
sortable: true,
|
||||
headers: customHeaders,
|
||||
rows: customRows,
|
||||
},
|
||||
});
|
||||
|
||||
// Verify custom display formatting
|
||||
const costCells = container.querySelectorAll("td:nth-child(2)");
|
||||
expect(costCells[0]).toHaveTextContent("100 €");
|
||||
expect(costCells[1]).toHaveTextContent("200 €");
|
||||
|
||||
// Test sorting by expireDate
|
||||
const dateHeader = screen.getByText("Expire date");
|
||||
await user.click(dateHeader);
|
||||
|
||||
// Verify rows are sorted by date ascending
|
||||
const rowsAfterDateSort = container.querySelectorAll("tbody tr");
|
||||
expect(rowsAfterDateSort[0].querySelector("td")).toHaveTextContent(
|
||||
"Load Balancer 2",
|
||||
);
|
||||
expect(rowsAfterDateSort[2].querySelector("td")).toHaveTextContent(
|
||||
"Load Balancer 3",
|
||||
);
|
||||
});
|
||||
|
||||
it("handles sorting with nested object values", async () => {
|
||||
const nestedHeaders = [
|
||||
{ key: "name", value: "Name" },
|
||||
{ key: "network.protocol", value: "Protocol" },
|
||||
{ key: "network.port", value: "Port" },
|
||||
] as const;
|
||||
|
||||
const nestedRows = [
|
||||
{
|
||||
id: "a",
|
||||
name: "Load Balancer 1",
|
||||
network: { protocol: "HTTP", port: 3000 },
|
||||
},
|
||||
{
|
||||
id: "b",
|
||||
name: "Load Balancer 2",
|
||||
network: { protocol: "HTTPS", port: 443 },
|
||||
},
|
||||
{
|
||||
id: "c",
|
||||
name: "Load Balancer 3",
|
||||
network: { protocol: "HTTP", port: 80 },
|
||||
},
|
||||
];
|
||||
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
sortable: true,
|
||||
headers: nestedHeaders,
|
||||
rows: nestedRows,
|
||||
},
|
||||
});
|
||||
|
||||
// Test sorting by nested port value
|
||||
const portHeader = screen.getByText("Port");
|
||||
await user.click(portHeader);
|
||||
|
||||
// Verify rows are sorted by port ascending
|
||||
const rowsAfterPortSort = container.querySelectorAll("tbody tr");
|
||||
expect(rowsAfterPortSort[0].querySelectorAll("td")[2]).toHaveTextContent(
|
||||
"80",
|
||||
);
|
||||
expect(rowsAfterPortSort[1].querySelectorAll("td")[2]).toHaveTextContent(
|
||||
"443",
|
||||
);
|
||||
expect(rowsAfterPortSort[2].querySelectorAll("td")[2]).toHaveTextContent(
|
||||
"3000",
|
||||
);
|
||||
});
|
||||
|
||||
it("handles disabled sorting on specific columns", async () => {
|
||||
const customHeaders = [
|
||||
{ key: "name", value: "Name" },
|
||||
{ key: "protocol", value: "Protocol", sort: false as const },
|
||||
{ key: "port", value: "Port" },
|
||||
];
|
||||
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
sortable: true,
|
||||
headers: customHeaders,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
const protocolHeader = screen.getByText("Protocol");
|
||||
await user.click(protocolHeader);
|
||||
|
||||
// Verify no sorting occurred
|
||||
const firstRow = container.querySelector("tbody tr:first-child");
|
||||
assert(firstRow);
|
||||
expect(firstRow.querySelector("td:first-child")).toHaveTextContent(
|
||||
"Load Balancer 3",
|
||||
);
|
||||
});
|
||||
|
||||
it("handles table with numeric sorting", async () => {
|
||||
const numericRows = [
|
||||
{ id: "a", value: 10 },
|
||||
{ id: "b", value: 2 },
|
||||
{ id: "c", value: 20 },
|
||||
];
|
||||
|
||||
const numericHeaders = [{ key: "value", value: "Value" }];
|
||||
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
sortable: true,
|
||||
headers: numericHeaders,
|
||||
rows: numericRows,
|
||||
},
|
||||
});
|
||||
|
||||
const valueHeader = screen.getByText("Value");
|
||||
await user.click(valueHeader);
|
||||
|
||||
const sortedCells = container.querySelectorAll("td");
|
||||
expect(sortedCells[0]).toHaveTextContent("2");
|
||||
expect(sortedCells[1]).toHaveTextContent("10");
|
||||
expect(sortedCells[2]).toHaveTextContent("20");
|
||||
});
|
||||
|
||||
// Selection tests
|
||||
it("handles selectable rows", async () => {
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
selectable: true,
|
||||
headers,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
// Verify checkboxes are present in each row
|
||||
const checkboxes = container.querySelectorAll("input[type='checkbox']");
|
||||
expect(checkboxes.length).toBe(3);
|
||||
|
||||
// Select first row
|
||||
await user.click(checkboxes[0]);
|
||||
|
||||
// Verify row is selected
|
||||
const selectedRow = container.querySelector(".bx--data-table--selected");
|
||||
expect(selectedRow).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("handles batch selection", async () => {
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
batchSelection: true,
|
||||
headers,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
// Verify batch selection checkbox is present
|
||||
const batchCheckbox = container.querySelector(
|
||||
".bx--table-column-checkbox input[type='checkbox']",
|
||||
);
|
||||
assert(batchCheckbox);
|
||||
|
||||
// Click batch selection checkbox
|
||||
await user.click(batchCheckbox);
|
||||
|
||||
// Verify all rows are selected
|
||||
const selectedRows = container.querySelectorAll(
|
||||
".bx--data-table--selected",
|
||||
);
|
||||
expect(selectedRows.length).toBe(3);
|
||||
|
||||
// Click batch selection checkbox again
|
||||
await user.click(batchCheckbox);
|
||||
|
||||
// Verify no rows are selected
|
||||
const unselectedRows = container.querySelectorAll(
|
||||
".bx--data-table--selected",
|
||||
);
|
||||
expect(unselectedRows.length).toBe(0);
|
||||
});
|
||||
|
||||
it("handles radio selection", async () => {
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
selectable: true,
|
||||
radio: true,
|
||||
headers,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
const radioButtons = container.querySelectorAll("input[type='radio']");
|
||||
expect(radioButtons.length).toBe(3);
|
||||
|
||||
await user.click(radioButtons[0]);
|
||||
const selectedRow = container.querySelector(".bx--data-table--selected");
|
||||
expect(selectedRow).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("handles non-selectable and non-expandable rows", async () => {
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
selectable: true,
|
||||
expandable: true,
|
||||
nonSelectableRowIds: ["b"],
|
||||
nonExpandableRowIds: ["c"],
|
||||
headers,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
// Verify non-selectable row doesn't have a checkbox
|
||||
const nonSelectableRow = container.querySelector("tr[data-row='b']");
|
||||
const nonSelectableCheckbox = nonSelectableRow?.querySelector(
|
||||
"input[type='checkbox']",
|
||||
);
|
||||
expect(nonSelectableCheckbox).not.toBeInTheDocument();
|
||||
|
||||
// Verify non-expandable row doesn't have an expand button
|
||||
const nonExpandableRow = container.querySelector("tr[data-row='c']");
|
||||
const nonExpandableButton = nonExpandableRow?.querySelector(
|
||||
".bx--table-expand__button",
|
||||
);
|
||||
expect(nonExpandableButton).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Expandable rows tests
|
||||
it("handles expandable rows", async () => {
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
expandable: true,
|
||||
headers,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
// Verify expand button is present in each row
|
||||
const expandButtons = container.querySelectorAll(
|
||||
".bx--table-expand__button",
|
||||
);
|
||||
expect(expandButtons.length).toBe(3);
|
||||
|
||||
// Click expand button on first row
|
||||
await user.click(expandButtons[0]);
|
||||
|
||||
const expandedRow = container.querySelector(".bx--expandable-row");
|
||||
expect(expandedRow).toBeInTheDocument();
|
||||
expect(expandedRow).toHaveClass("bx--expandable-row bx--parent-row");
|
||||
});
|
||||
|
||||
it("handles batch expansion", async () => {
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
expandable: true,
|
||||
batchExpansion: true,
|
||||
headers,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
container.querySelectorAll(".bx--child-row-inner-container"),
|
||||
).toHaveLength(0);
|
||||
|
||||
const expandAllButton = screen.getByLabelText("Expand all rows");
|
||||
|
||||
await user.click(expandAllButton);
|
||||
expect(
|
||||
container.querySelectorAll(".bx--child-row-inner-container"),
|
||||
).toHaveLength(3);
|
||||
|
||||
await user.click(expandAllButton);
|
||||
expect(
|
||||
container.querySelectorAll(".bx--child-row-inner-container"),
|
||||
).toHaveLength(0);
|
||||
});
|
||||
|
||||
// Styling and layout tests
|
||||
it("applies zebra stripe styling", async () => {
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
zebra: true,
|
||||
headers,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
// Verify zebra stripe classes are applied
|
||||
const table = container.querySelector("table");
|
||||
expect(table).toHaveClass("bx--data-table--zebra");
|
||||
});
|
||||
|
||||
it("applies different size variants", async () => {
|
||||
type Size = "compact" | "short" | "medium" | "tall";
|
||||
const sizeMappings: Record<Size, string> = {
|
||||
compact: "bx--data-table--compact",
|
||||
short: "bx--data-table--short",
|
||||
medium: "bx--data-table--md",
|
||||
tall: "bx--data-table--tall",
|
||||
};
|
||||
|
||||
for (const [size, expectedClass] of Object.entries(sizeMappings)) {
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
size: size as Size,
|
||||
headers,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
// Verify size class is applied
|
||||
const table = container.querySelector("table");
|
||||
expect(table).toHaveClass(expectedClass);
|
||||
}
|
||||
});
|
||||
|
||||
it("applies custom column widths", async () => {
|
||||
const customHeaders = [
|
||||
{ key: "name", value: "Name", width: "200px" },
|
||||
{ key: "protocol", value: "Protocol", minWidth: "100px" },
|
||||
{ key: "port", value: "Port" },
|
||||
{ key: "rule", value: "Rule" },
|
||||
] as const;
|
||||
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
headers: customHeaders,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
// Verify table has fixed layout
|
||||
const table = container.querySelector("table");
|
||||
expect(table).toHaveStyle({ "table-layout": "fixed" });
|
||||
|
||||
// Verify name column has correct width
|
||||
const nameHeader = container.querySelector("th:first-child");
|
||||
expect(nameHeader).toHaveStyle({ width: "200px" });
|
||||
|
||||
// Verify protocol column has correct min-width
|
||||
const protocolHeader = container.querySelector("th:nth-child(2)");
|
||||
expect(protocolHeader).toHaveStyle({ "min-width": "100px" });
|
||||
});
|
||||
|
||||
it("applies sticky header", async () => {
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
stickyHeader: true,
|
||||
headers,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
const table = container.querySelector("table");
|
||||
expect(table).toHaveClass("bx--data-table--sticky-header");
|
||||
});
|
||||
|
||||
it("handles static width", () => {
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
useStaticWidth: true,
|
||||
headers,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
const tableContainer = container.querySelector(".bx--data-table-container");
|
||||
expect(tableContainer).toHaveClass("bx--data-table-container--static");
|
||||
|
||||
const table = container.querySelector("table");
|
||||
expect(table).toHaveClass("bx--data-table--static");
|
||||
});
|
||||
|
||||
// Custom cell display tests
|
||||
it("handles custom cell display", () => {
|
||||
const customHeaders = [
|
||||
{ key: "name", value: "Name" },
|
||||
{
|
||||
key: "port",
|
||||
value: "Port",
|
||||
display: (value: number) => `Port ${value}`,
|
||||
},
|
||||
];
|
||||
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
headers: customHeaders,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
const portCells = container.querySelectorAll("td:nth-child(2)");
|
||||
expect(portCells[0]).toHaveTextContent("Port 3000");
|
||||
expect(portCells[1]).toHaveTextContent("Port 443");
|
||||
expect(portCells[2]).toHaveTextContent("Port 80");
|
||||
});
|
||||
|
||||
it("handles empty columns for custom content", () => {
|
||||
const emptyColumnHeaders = [
|
||||
{ key: "name", value: "Name" },
|
||||
{ key: "protocol", value: "Protocol" },
|
||||
{ key: "port", value: "Port" },
|
||||
{ key: "rule", value: "Rule" },
|
||||
{ key: "actions", value: "", empty: true },
|
||||
] as const;
|
||||
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
headers: emptyColumnHeaders,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
// Verify empty column header exists and has no text content
|
||||
const headerCells = screen.getAllByRole("columnheader");
|
||||
expect(headerCells.length).toBe(5); // 4 regular headers + 1 empty column
|
||||
expect(headerCells[4]).toHaveTextContent("");
|
||||
|
||||
// Verify empty column cells exist in each row
|
||||
const tableRows = container.querySelectorAll("tbody tr");
|
||||
tableRows.forEach((row) => {
|
||||
const cells = row.querySelectorAll("td");
|
||||
expect(cells.length).toBe(5);
|
||||
});
|
||||
});
|
||||
|
||||
// Pagination tests
|
||||
it("handles pagination", async () => {
|
||||
const paginatedRows = Array.from({ length: 15 }).map((_, i) => ({
|
||||
id: `row-${i}`,
|
||||
name: `Load Balancer ${i + 1}`,
|
||||
protocol: "HTTP",
|
||||
port: 3000 + i,
|
||||
rule: i % 2 ? "Round robin" : "DNS delegation",
|
||||
}));
|
||||
|
||||
const { container, component } = render(DataTable, {
|
||||
props: {
|
||||
headers,
|
||||
rows: paginatedRows,
|
||||
pageSize: 5,
|
||||
page: 1,
|
||||
},
|
||||
});
|
||||
|
||||
// Verify only 5 rows are displayed on first page
|
||||
const firstPageRows = container.querySelectorAll("tbody tr");
|
||||
expect(firstPageRows.length).toBe(5);
|
||||
expect(firstPageRows[0].querySelector("td")).toHaveTextContent(
|
||||
"Load Balancer 1",
|
||||
);
|
||||
expect(firstPageRows[4].querySelector("td")).toHaveTextContent(
|
||||
"Load Balancer 5",
|
||||
);
|
||||
|
||||
// Update page to 2
|
||||
component.$set({ page: 2 });
|
||||
await tick();
|
||||
|
||||
// Verify 5 rows are displayed on second page
|
||||
const secondPageRows = container.querySelectorAll("tbody tr");
|
||||
expect(secondPageRows.length).toBe(5);
|
||||
expect(secondPageRows[0].querySelector("td")).toHaveTextContent(
|
||||
"Load Balancer 6",
|
||||
);
|
||||
expect(secondPageRows[4].querySelector("td")).toHaveTextContent(
|
||||
"Load Balancer 10",
|
||||
);
|
||||
|
||||
// Update page to 3
|
||||
component.$set({ page: 3 });
|
||||
await tick();
|
||||
|
||||
// Verify remaining rows are displayed on third page
|
||||
const thirdPageRows = container.querySelectorAll("tbody tr");
|
||||
expect(thirdPageRows.length).toBe(5);
|
||||
expect(thirdPageRows[0].querySelector("td")).toHaveTextContent(
|
||||
"Load Balancer 11",
|
||||
);
|
||||
expect(thirdPageRows[4].querySelector("td")).toHaveTextContent(
|
||||
"Load Balancer 15",
|
||||
);
|
||||
});
|
||||
|
||||
// Event handling tests
|
||||
it("emits proper events", async () => {
|
||||
const consoleLog = vi.spyOn(console, "log");
|
||||
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
headers,
|
||||
rows,
|
||||
sortable: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Click header
|
||||
const nameHeader = screen.getByText("Name");
|
||||
await user.click(nameHeader);
|
||||
|
||||
// Click row
|
||||
const firstRow = container.querySelector("tbody tr");
|
||||
assert(firstRow);
|
||||
await user.click(firstRow);
|
||||
|
||||
// Click cell
|
||||
const firstCell = container.querySelector("td");
|
||||
assert(firstCell);
|
||||
await user.click(firstCell);
|
||||
|
||||
// Verify events were logged
|
||||
expect(consoleLog).toHaveBeenCalledWith("click:header", expect.any(Object));
|
||||
expect(consoleLog).toHaveBeenCalledWith("click:row", expect.any(Object));
|
||||
expect(consoleLog).toHaveBeenCalledWith("click:cell", expect.any(Object));
|
||||
});
|
||||
|
||||
it("handles row hover events", async () => {
|
||||
const consoleLog = vi.spyOn(console, "log");
|
||||
const { container } = render(DataTable, {
|
||||
props: {
|
||||
headers,
|
||||
rows,
|
||||
},
|
||||
});
|
||||
|
||||
const firstRow = container.querySelector("tbody tr");
|
||||
assert(firstRow);
|
||||
await user.hover(firstRow);
|
||||
await user.unhover(firstRow);
|
||||
|
||||
expect(consoleLog).toHaveBeenCalledWith(
|
||||
"mouseenter:row",
|
||||
expect.any(Object),
|
||||
);
|
||||
expect(consoleLog).toHaveBeenCalledWith(
|
||||
"mouseleave:row",
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue