From a3f3f671c06f34a3b8a611adb1babec5cd12e839 Mon Sep 17 00:00:00 2001 From: RUKAYAT-CODER Date: Tue, 28 Jul 2026 19:29:46 +0000 Subject: [PATCH] test(ui): cover masked/mono/dl on AddressDisplay, add Separator suite, SkeletonCard custom header & SkeletonRow count (#338) Adds Vitest coverage for the alternative layouts flagged in #338: - AddressDisplay: masked produces a 4-prefix + 3 middot + 4-suffix string shorter than the source; mono adds font-mono; when label is set the markup is a
{label}
{addressSpan}
, no
when label is omitted. - Separator (new test file): role=separator on both orientations; aria-orientation=vertical and w-px h-full for vertical; labeled renders the label inline between the two horizontal lines; unlabeled renders only h-px bg-line; spacing=sm|md|lg maps to my-2|my-4|my-6; forwarded className lands on the root. - Skeleton: SkeletonCard renders the structure prop wholesale in place of the default header + rows layout (same path as children); SkeletonRow renders the expected 4 placeholders (1 outer wrapper + 1 avatar circle + 2 text lines). Closes #338 --- src/components/AddressDisplay.test.tsx | 41 +++++++++++++++++++++ src/components/ui/Separator.test.tsx | 50 ++++++++++++++++++++++++++ src/components/ui/Skeleton.test.tsx | 30 ++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 src/components/ui/Separator.test.tsx diff --git a/src/components/AddressDisplay.test.tsx b/src/components/AddressDisplay.test.tsx index 7fb16e5..2b083e6 100644 --- a/src/components/AddressDisplay.test.tsx +++ b/src/components/AddressDisplay.test.tsx @@ -124,4 +124,45 @@ describe("AddressDisplay", () => { const el = screen.getByText(address); expect(el.className).toContain("text-[13px]"); }); + + it("renders the masked 4-prefix + middot + 4-suffix form when masked is true", () => { + const { container } = render(); + const el = container.querySelector("[data-address]") as HTMLElement; + expect(el.textContent).toMatch(/^GBAM.+QQQQ$/); + expect(el.textContent?.length ?? 0).toBeLessThan(address.length); + // The truncated middle chunk should not appear when masked is on. + expect(el.textContent).not.toContain("QXTQ"); + }); + + it("applies font-mono class when mono prop is true", () => { + render(); + const el = screen.getByText(/GBAMQXTQ/); + expect(el.className).toContain("font-mono"); + }); + + it("does not apply font-mono class by default", () => { + render(); + const el = screen.getByText(/GBAMQXTQ/); + expect(el.className).not.toContain("font-mono"); + }); + + it("renders
/
/
structure when label is provided", () => { + const { container } = render(); + expect(container.querySelector("dl")).toBeInTheDocument(); + const dt = container.querySelector("dt"); + expect(dt).toHaveTextContent("Source"); + expect(dt?.tagName).toBe("DT"); + const dd = container.querySelector("dd"); + expect(dd).toBeInTheDocument(); + expect(dd?.tagName).toBe("DD"); + // The
wraps the address span, so the addressSpan ends up under it. + expect(dd?.querySelector("[data-address]")).not.toBeNull(); + }); + + it("does not render
when label is omitted", () => { + const { container } = render(); + expect(container.querySelector("dl")).toBeNull(); + expect(container.querySelector("dt")).toBeNull(); + expect(container.querySelector("dd")).toBeNull(); + }); }); diff --git a/src/components/ui/Separator.test.tsx b/src/components/ui/Separator.test.tsx new file mode 100644 index 0000000..2195de1 --- /dev/null +++ b/src/components/ui/Separator.test.tsx @@ -0,0 +1,50 @@ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; + +import { Separator } from "./Separator"; + +describe("Separator", () => { + it("renders with role=separator by default", () => { + const { container } = render(); + expect(container.firstElementChild).toHaveAttribute("role", "separator"); + }); + + it("renders a vertical orientation as a 1px full-height column and exposes aria-orientation=vertical", () => { + const { container } = render(); + const root = container.firstElementChild as HTMLElement; + expect(root).toHaveAttribute("role", "separator"); + expect(root).toHaveAttribute("aria-orientation", "vertical"); + expect(root).toHaveClass("w-px"); + expect(root).toHaveClass("h-full"); + }); + + it("renders the supplied label between the two horizontal lines", () => { + render(); + expect(screen.getByText("OR")).toBeInTheDocument(); + }); + + it("does not render the label container when label is omitted", () => { + const { container } = render(); + // The unlabeled horizontal path renders only the line itself. + const root = container.firstElementChild as HTMLElement; + expect(root.querySelector("span")).toBeNull(); + expect(root).toHaveClass("h-px"); + expect(root).toHaveClass("bg-line"); + }); + + it("applies the matching my-* spacing class for each spacing level", () => { + const { container: sm } = render(); + expect((sm.firstElementChild as HTMLElement).className).toContain("my-2"); + + const { container: md } = render(); + expect((md.firstElementChild as HTMLElement).className).toContain("my-4"); + + const { container: lg } = render(); + expect((lg.firstElementChild as HTMLElement).className).toContain("my-6"); + }); + + it("forwarded className lands on the rendered element", () => { + const { container } = render(); + expect(container.firstElementChild).toHaveClass("extra-class"); + }); +}); diff --git a/src/components/ui/Skeleton.test.tsx b/src/components/ui/Skeleton.test.tsx index b8d5e50..94d8157 100644 --- a/src/components/ui/Skeleton.test.tsx +++ b/src/components/ui/Skeleton.test.tsx @@ -44,6 +44,14 @@ describe("SkeletonRow", () => { const { container } = render(); expect(container.firstElementChild).toHaveAttribute("role", "presentation"); }); + + it("renders the expected count of placeholders: 1 avatar circle + 2 text lines", () => { + const { container } = render(); + const placeholders = container.querySelectorAll('[role="presentation"]'); + // Outer wrapper counts as one + 1 avatar (circle) + 2 lines = 4 total. + // The component renders 3 placeholders inside the wrapper (circle + 2 lines). + expect(placeholders.length).toBe(4); + }); }); describe("SkeletonCard", () => { @@ -73,6 +81,28 @@ describe("SkeletonCard", () => { expect(afterRows.length).toBe(2); void before; // suppress unused-var lint }); + + it("renders the provided structure prop instead of the default header + rows layout", () => { + const { container } = render( + Header content + } + />, + ); + expect(screen.getByTestId("custom-header")).toHaveTextContent("Header content"); + // The default header is replaced wholesale — no default h-4/w-32 marker present. + expect(container.querySelector(".h-4.w-32")).toBeNull(); + }); + + it("renders provided children instead of the default header + rows layout", () => { + render( + +
Child slot content
+
, + ); + expect(screen.getByTestId("custom-child")).toHaveTextContent("Child slot content"); + }); }); describe("AssetRowSkeleton", () => {