import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { StatusPill, type StatusTone } from "./status-pill";
const TONES: StatusTone[] = [
"success",
"warning",
"danger",
"info",
"running",
"neutral",
];
describe("StatusPill", () => {
it("states the status as text, not colour alone", () => {
render(Failed);
expect(screen.getByText("Failed")).toBeInTheDocument();
});
// The glyph is the channel that survives greyscale and colour blindness, so
// every tone must carry one and it must be hidden from the accessibility tree
// — the label beside it already says the same thing.
it.each(TONES)("gives %s a decorative glyph", (tone) => {
const { container } = render(Status);
const glyph = container.querySelector("svg");
expect(glyph).not.toBeNull();
expect(glyph).toHaveAttribute("aria-hidden", "true");
});
it.each(TONES)("brings %s's own paired background", (tone) => {
const { container } = render(Status);
const pill = container.firstElementChild as HTMLElement;
expect(pill.className).toContain(`bg-[var(--surface-`);
expect(pill.className).toContain("border");
});
// A tone colour is solved against its own background. `bare` drops that
// background, so the tone may ride on the glyph only — lifting it onto the
// label would put it on whatever plane the caller happens to be on.
it.each(TONES)("keeps %s's tone off the label in bare mode", (tone) => {
const { container } = render(
Status
,
);
const pill = container.firstElementChild as HTMLElement;
expect(pill.className).toContain("bg-transparent");
expect(pill.className).not.toContain("bg-[var(--surface-");
const glyphWrapper = container.querySelector("svg")
?.parentElement as HTMLElement;
expect(glyphWrapper.className).toContain("text-[var(--surface-");
});
// Guards the reason TONE_TEXT is declared rather than recovered by searching
// TONE_SURFACE for a `text-` class: a miss there yields undefined and removes
// the tone from the only channel bare mode has left.
it.each(TONES)("resolves a real text class for %s in bare mode", (tone) => {
const { container } = render(
Status
,
);
const glyphWrapper = container.querySelector("svg")
?.parentElement as HTMLElement;
expect(glyphWrapper.className).not.toContain("undefined");
expect(glyphWrapper.className).toMatch(/text-\[var\(--surface-[a-z]+-text\)\]/);
});
// `px-2` from the size and `px-0` from bare are the same utility group, so
// which one lands is decided by the class merge rather than by reading order.
// Asserting the resolved class list keeps that a guarantee: a bare pill has to
// sit flush against the text beside it, and silently keeping the size padding
// would space it like a chip that lost only its background.
it.each(["sm", "md"] as const)("drops the %s size padding in bare mode", (size) => {
const { container } = render(
Queued
,
);
const cls = (container.firstElementChild as HTMLElement).className;
expect(cls).toContain("px-0");
expect(cls).not.toMatch(/(^| )px-2(\.5)?( |$)/);
});
it("gives no two tones the same silhouette", () => {
const shapes = TONES.map((tone) => {
const { container } = render(S);
return container.querySelector("svg")?.innerHTML ?? "";
});
expect(new Set(shapes).size).toBe(shapes.length);
});
it("takes a larger size", () => {
const { container } = render(
Queued
,
);
expect((container.firstElementChild as HTMLElement).className).toContain(
"text-sm",
);
});
});