import { createRawSnippet } from "svelte";
import { describe, expect, it, vi } from "vitest";
import StatusVocabularyConsumer from "./fixtures/StatusVocabularyConsumer.svelte";
import {
BADGE_VARIANTS,
Badge,
DEPRECATED_BADGE_VARIANTS,
TONES,
badge,
} from "./fixtures/status-vocabulary-consumer";
import { classes, classesOf, render, rendered } from "./mount";
const label = (value: string) => createRawSnippet(() => ({ render: () => `${value}` }));
describe("the Base Badge", () => {
it("is available with every emphasis and its Extension Seam through Base", () => {
expect(Badge).toBeDefined();
expect(badge).toBeTypeOf("function");
// Emphasis (`variant`) and meaning (`tone`) are two axes (ADR 0026).
expect(BADGE_VARIANTS).toEqual(["tinted", "filled", "outline"]);
expect(Object.keys(DEPRECATED_BADGE_VARIANTS)).toEqual(["neutral", "primary"]);
});
it("draws every tone at every emphasis from its Feedback Role, never the accent", () => {
for (const variant of BADGE_VARIANTS) {
for (const tone of TONES) {
const element = rendered(render(Badge, { tone, variant, children: label(tone) }));
const worn = [...classes(element)];
expect(classes(element), `${tone}/${variant}`).toEqual(classesOf(badge({ tone, variant })));
expect(element.dataset.tone).toBe(tone);
expect(worn.some((name) => /(?:^|-)primary$/.test(name)), `${tone}/${variant}`).toBe(false);
if (tone !== "neutral") {
expect(worn.some((name) => name.includes(`feedback-${tone}-`)), `${tone}/${variant}`).toBe(true);
}
}
}
// Tinted is the role's copy on its tinted surface; filled is a tone chip.
expect(classesOf(badge({ tone: "success" }))).toEqual(
classesOf(badge({ tone: "success", variant: "tinted" })),
);
expect([...classesOf(badge({ tone: "danger", variant: "filled" }))]).toEqual(
expect.arrayContaining(["bg-feedback-danger-foreground", "text-on-feedback"]),
);
});
it("keeps the deprecated neutral and primary variants working for one release, with a warning", () => {
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
try {
const neutral = rendered(render(Badge, { variant: "neutral", children: label("Draft") }));
expect(classes(neutral)).toEqual(classesOf(badge({ variant: "tinted" })));
const primary = rendered(render(Badge, { variant: "primary", children: label("New") }));
expect([...classes(primary)]).toEqual(expect.arrayContaining(["bg-primary", "text-on-primary"]));
rendered(render(Badge, { variant: "primary", children: label("Again") }));
expect(warn).toHaveBeenCalledTimes(2);
expect(String(warn.mock.calls[0]![0])).toMatch(/\[reddb Badge\] variant="neutral" is deprecated/);
expect(String(warn.mock.calls[1]![0])).toMatch(/\[reddb Badge\] variant="primary" is deprecated.*variant="filled"/);
} finally {
warn.mockRestore();
}
});
it("renders required caller-owned status text inside running text", () => {
for (const variant of BADGE_VARIANTS) {
const element = rendered(render(Badge, { variant, children: label(`${variant} release`) }));
expect(element.tagName).toBe("SPAN");
expect(element.textContent).toBe(`${variant} release`);
expect(classes(element)).toEqual(classesOf(badge({ variant })));
}
});
it("does not become a control while preserving native attributes", () => {
const element = rendered(render(Badge, {
children: label("Beta"),
id: "release-stage",
title: "Release stage",
}));
expect(element.tabIndex).toBe(-1);
expect(element.id).toBe("release-stage");
expect(element.title).toBe("Release stage");
});
it("keeps its Density role live and inherits every nested appearance axis", () => {
const element = rendered(render(Badge, {
children: label("Stable"),
class: "uppercase",
}));
const nested = rendered(render(StatusVocabularyConsumer, {})).querySelector(
"[data-badge]",
)!;
expect(classes(element)).toEqual(classesOf(badge({ class: "uppercase" })));
expect(classes(element).has("gap-[var(--reddb-spatial-gap-sm)]")).toBe(true);
for (const root of [element, nested]) {
expect(root.hasAttribute("data-theme")).toBe(false);
expect(root.hasAttribute("data-color-scheme")).toBe(false);
expect(root.hasAttribute("data-density")).toBe(false);
}
});
});