dodil/erp-ui
tests/detail-and-timeline.test.tsx
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { DetailPage, FilterBar, Timeline } from "../src";
import { a11y, renderInTheme } from "./helpers";
test("a detail page: fields formatted, an unavailable action says why, panels from other modules", async () => {
const sent: string[] = [];
const { container } = renderInTheme(
<DetailPage
title="Acme AB" subtitle="acme.com · SE" status="blocked"
actions={[{ label: "Unblock", onClick: () => sent.push("unblock"), tone: "primary" },
{ label: "Send quote", onClick: () => sent.push("send"), disabledReason: "Acme AB is blocked" }]}
sections={[{ title: "Partner", fields: [{ label: "Tax id", value: "SE5560123401" },
{ label: "Open pipeline", value: "2460", kind: "money", currency: "EUR" }] }]}
panels={[{ title: "Timeline", content: <p>calls here</p> }]}
/>,
);
expect(screen.getByRole("heading", { name: "Acme AB", level: 1 })).toBeInTheDocument();
expect(screen.getByText("2,460.00 EUR")).toBeInTheDocument();
const send = screen.getByRole("button", { name: "Send quote" });
expect(send).toBeDisabled();
expect(send).toHaveAttribute("aria-description", "Acme AB is blocked");
await userEvent.click(screen.getByRole("button", { name: "Unblock" }));
expect(sent).toEqual(["unblock"]);
expect(screen.getByRole("region", { name: "Timeline" })).toHaveTextContent("calls here");
expect(await a11y(container)).toEqual([]);
});
test("not found is the same answer the API gives", () => {
renderInTheme(<DetailPage notFound />);
expect(screen.getByText("Not found")).toBeInTheDocument();
});
test("the timeline is newest first, grouped by day", async () => {
const { container } = renderInTheme(<Timeline items={[
{ id: "1", kind: "CALL", title: "first call", at: "2026-10-01T09:00:00Z" },
{ id: "2", kind: "NOTE", title: "about Acme", at: "2026-10-05T09:00:00Z" },
{ id: "3", kind: "TASK", title: "send proposal", at: "2026-10-05T12:00:00Z", status: "open" },
]} />);
expect(screen.getAllByRole("listitem").filter((li) => li.querySelector("ul") === null).map((li) => li.textContent))
.toEqual([expect.stringContaining("send proposal"), expect.stringContaining("about Acme"), expect.stringContaining("first call")]);
expect(await a11y(container)).toEqual([]);
});
test("the filter bar is controlled and clears everything", async () => {
const state = { q: "acme", role: "customer" as string | null };
renderInTheme(<FilterBar search={{ value: state.q, onChange: (v) => (state.q = v) }}
filters={[{ key: "role", label: "Role", value: state.role, onChange: (v) => (state.role = v),
options: [{ value: "customer", label: "Customer" }] }]} />);
await userEvent.click(screen.getByRole("button", { name: "Clear" }));
expect(state).toEqual({ q: "", role: null });
});