175 lines
5.9 KiB
TypeScript
175 lines
5.9 KiB
TypeScript
import { fireEvent, render, screen } from "@solidjs/testing-library";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { DataTable, DataTableColumnSchema, DataTableMeta, DataTableRootPropsSchema } from "../../../src/components/data-table/index";
|
|
|
|
const data = [
|
|
{ id: 1, name: "Alice", age: 30 },
|
|
{ id: 2, name: "Bob", age: 25 },
|
|
{ id: 3, name: "Charlie", age: 35 },
|
|
];
|
|
|
|
const columns = [
|
|
{ id: "name", header: "Name", cell: (row: typeof data[0]) => row.name, sortable: true },
|
|
{ id: "age", header: "Age", cell: (row: typeof data[0]) => String(row.age), sortable: true },
|
|
];
|
|
|
|
function TestTable(props: { pageSize?: number; enableRowSelection?: boolean }) {
|
|
return (
|
|
<DataTable data={data} columns={columns} pageSize={props.pageSize ?? 10} enableRowSelection={props.enableRowSelection}>
|
|
<table>
|
|
<DataTable.Header />
|
|
<DataTable.Body />
|
|
</table>
|
|
<DataTable.Pagination />
|
|
</DataTable>
|
|
);
|
|
}
|
|
|
|
describe("DataTable — rendering", () => {
|
|
it("renders all rows by default", () => {
|
|
render(() => <TestTable />);
|
|
expect(screen.getByText("Alice")).toBeTruthy();
|
|
expect(screen.getByText("Bob")).toBeTruthy();
|
|
expect(screen.getByText("Charlie")).toBeTruthy();
|
|
});
|
|
|
|
it("renders column headers", () => {
|
|
render(() => <TestTable />);
|
|
expect(screen.getByText("Name")).toBeTruthy();
|
|
expect(screen.getByText("Age")).toBeTruthy();
|
|
});
|
|
|
|
it("renders pagination controls", () => {
|
|
render(() => <TestTable />);
|
|
expect(screen.getByText("Previous")).toBeTruthy();
|
|
expect(screen.getByText("Next")).toBeTruthy();
|
|
expect(screen.getByText(/Page 1 of/)).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
describe("DataTable — sorting", () => {
|
|
it("sorts ascending on first header click", () => {
|
|
render(() => <TestTable />);
|
|
fireEvent.click(screen.getByText("Name"));
|
|
const cells = screen.getAllByRole("cell");
|
|
const names = cells.filter((_, i) => i % 2 === 0).map((c) => c.textContent);
|
|
expect(names[0]).toBe("Alice");
|
|
expect(names[1]).toBe("Bob");
|
|
expect(names[2]).toBe("Charlie");
|
|
});
|
|
|
|
it("sorts descending on second header click", () => {
|
|
render(() => <TestTable />);
|
|
const nameHeader = screen.getByRole("columnheader", { name: /Name/ });
|
|
fireEvent.click(nameHeader);
|
|
fireEvent.click(nameHeader);
|
|
const cells = screen.getAllByRole("cell");
|
|
const names = cells.filter((_, i) => i % 2 === 0).map((c) => c.textContent);
|
|
expect(names[0]).toBe("Charlie");
|
|
expect(names[1]).toBe("Bob");
|
|
expect(names[2]).toBe("Alice");
|
|
});
|
|
|
|
it("resets sort on third header click", () => {
|
|
render(() => <TestTable />);
|
|
const nameHeader = screen.getByRole("columnheader", { name: /Name/ });
|
|
fireEvent.click(nameHeader);
|
|
fireEvent.click(nameHeader);
|
|
fireEvent.click(nameHeader);
|
|
expect(nameHeader.getAttribute("aria-sort")).toBe("none");
|
|
});
|
|
|
|
it("header has aria-sort=ascending when sorted asc", () => {
|
|
render(() => <TestTable />);
|
|
const nameHeader = screen.getByRole("columnheader", { name: /Name/ });
|
|
fireEvent.click(nameHeader);
|
|
expect(nameHeader.getAttribute("aria-sort")).toBe("ascending");
|
|
});
|
|
});
|
|
|
|
describe("DataTable — pagination", () => {
|
|
it("shows only pageSize rows per page", () => {
|
|
render(() => <TestTable pageSize={2} />);
|
|
const cells = screen.getAllByRole("cell");
|
|
expect(cells.length).toBe(4);
|
|
});
|
|
|
|
it("next page shows remaining rows", () => {
|
|
render(() => <TestTable pageSize={2} />);
|
|
fireEvent.click(screen.getByText("Next"));
|
|
expect(screen.getByText("Charlie")).toBeTruthy();
|
|
});
|
|
|
|
it("previous button is disabled on first page", () => {
|
|
render(() => <TestTable pageSize={2} />);
|
|
const prev = screen.getByText("Previous") as HTMLButtonElement;
|
|
expect(prev.disabled).toBe(true);
|
|
});
|
|
|
|
it("next button is disabled on last page", () => {
|
|
render(() => <TestTable pageSize={2} />);
|
|
fireEvent.click(screen.getByText("Next"));
|
|
const next = screen.getByText("Next") as HTMLButtonElement;
|
|
expect(next.disabled).toBe(true);
|
|
});
|
|
|
|
it("shows correct page label", () => {
|
|
render(() => <TestTable pageSize={2} />);
|
|
expect(screen.getByText("Page 1 of 2")).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
describe("DataTable — row selection", () => {
|
|
it("clicking a row marks it selected", () => {
|
|
render(() => <TestTable enableRowSelection />);
|
|
const rows = screen.getAllByRole("row");
|
|
fireEvent.click(rows[1]);
|
|
expect(rows[1].getAttribute("aria-selected")).toBe("true");
|
|
});
|
|
|
|
it("clicking selected row deselects it", () => {
|
|
render(() => <TestTable enableRowSelection />);
|
|
const rows = screen.getAllByRole("row");
|
|
fireEvent.click(rows[1]);
|
|
fireEvent.click(rows[1]);
|
|
expect(rows[1].getAttribute("aria-selected")).toBe("false");
|
|
});
|
|
|
|
it("onSelectionChange is called", () => {
|
|
const onChange = vi.fn();
|
|
render(() => (
|
|
<DataTable data={data} columns={columns} enableRowSelection onSelectionChange={onChange}>
|
|
<table>
|
|
<DataTable.Header />
|
|
<DataTable.Body />
|
|
</table>
|
|
<DataTable.Pagination />
|
|
</DataTable>
|
|
));
|
|
const rows = screen.getAllByRole("row");
|
|
fireEvent.click(rows[1]);
|
|
expect(onChange).toHaveBeenCalledWith(expect.any(Set));
|
|
});
|
|
});
|
|
|
|
describe("DataTable — schema and meta", () => {
|
|
it("DataTableColumnSchema validates a valid column", () => {
|
|
const result = DataTableColumnSchema.safeParse({ id: "name", header: "Name", sortable: true });
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it("DataTableRootPropsSchema validates pageSize", () => {
|
|
const result = DataTableRootPropsSchema.safeParse({ pageSize: 20 });
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it("DataTableMeta has correct name", () => {
|
|
expect(DataTableMeta.name).toBe("DataTable");
|
|
});
|
|
|
|
it("DataTableMeta lists required parts", () => {
|
|
expect(DataTableMeta.requiredParts).toContain("Root");
|
|
expect(DataTableMeta.requiredParts).toContain("Body");
|
|
});
|
|
});
|