import { createRawSnippet, type Snippet } from "svelte";
import { describe, expect, it } from "vitest";
import NotificationEmptyStateContractFailures from "./fixtures/NotificationEmptyStateContractFailures.svelte";
import NotificationEmptyStateConsumer from "./fixtures/NotificationEmptyStateConsumer.svelte";
import {
EMPTY_STATE_SIZES,
EmptyState,
emptyState,
} from "./fixtures/notification-empty-state-consumer";
import { classes, classesOf, render, rendered } from "./mount";
function content(markup: string): Snippet {
return createRawSnippet(() => ({ render: () => markup }));
}
describe("the deliberately unnamed empty-state fixture", () => {
it("demonstrates an action with no sentence saying what is empty", () => {
const empty = rendered(
render(NotificationEmptyStateContractFailures, { failure: "unnamed-empty-state" }),
);
expect(empty.querySelector("button")?.textContent).toBe("Add a node");
expect(empty.querySelector("p")).toBeNull();
});
});
describe("the Base EmptyState", () => {
it("renders the required sentence that names the empty region", () => {
const empty = rendered(render(EmptyState, { title: "No nodes yet" }));
expect(empty.textContent?.trim()).toBe("No nodes yet");
});
it("publishes complete size and border appearance without fixing an axis", () => {
expect(EMPTY_STATE_SIZES).toEqual(["sm", "md"]);
for (const size of EMPTY_STATE_SIZES) {
for (const bordered of [true, false]) {
const empty = rendered(render(EmptyState, { title: "Empty", size, bordered }));
expect(classes(empty)).toEqual(classesOf(emptyState({ size, bordered }).root()));
expect([...classes(empty)].join(" ")).toContain("var(--reddb-spatial-");
expect(empty.hasAttribute("data-theme")).toBe(false);
expect(empty.hasAttribute("data-color-scheme")).toBe(false);
expect(empty.hasAttribute("data-density")).toBe(false);
}
}
});
it("renders optional supporting copy only when the caller supplies it", () => {
const supported = rendered(
render(EmptyState, {
title: "No nodes yet",
description: "A node appears here as soon as one joins.",
}),
);
const bare = rendered(render(EmptyState, { title: "No nodes yet" }));
expect([...supported.querySelectorAll("p")].map(({ textContent }) => textContent)).toEqual([
"No nodes yet",
"A node appears here as soon as one joins.",
]);
expect(bare.querySelectorAll("p")).toHaveLength(1);
});
it("keeps caller media decorative because the required title carries the message", () => {
const empty = rendered(
render(EmptyState, { title: "No nodes yet", media: content("Empty box") }),
);
const media = empty.firstElementChild!;
expect(media.getAttribute("aria-hidden")).toBe("true");
expect(classes(media)).toEqual(classesOf(emptyState({}).media()));
});
it("renders a literal hint between its explanation and caller-owned actions", () => {
const empty = rendered(
render(EmptyState, {
title: "No nodes yet",
description: "A node appears here as soon as one joins.",
hint: "reddb node add --name alpha",
actions: content(''),
}),
);
const hint = empty.querySelector("code")!;
const children = [...empty.children];
expect(hint.textContent).toBe("reddb node add --name alpha");
expect(children.indexOf(hint)).toBeGreaterThan(children.indexOf(empty.querySelectorAll("p")[1]!));
expect(children.indexOf(hint)).toBeLessThan(children.length - 1);
});
it("preserves the caller's native action and keyboard focus order", () => {
const empty = rendered(
render(EmptyState, {
title: "No nodes yet",
actions: content(''),
}),
);
const action = empty.querySelector("button")!;
action.focus();
expect(document.activeElement).toBe(action);
expect(action.textContent).toBe("Add a node");
expect(action.closest("div")?.parentElement).toBe(empty);
});
it("merges caller classes and forwards native attributes", () => {
const empty = rendered(
render(EmptyState, { title: "No nodes yet", id: "nodes-empty", class: "max-w-lg" }),
);
expect(empty.id).toBe("nodes-empty");
expect(classes(empty)).toEqual(
classesOf(emptyState({ size: "md", bordered: true }).root({ class: "max-w-lg" })),
);
});
it("inherits every appearance axis from a nested consumer scope", () => {
const nested = render(NotificationEmptyStateConsumer)
.querySelector("[data-nested-empty-state] > div")!;
expect(nested.hasAttribute("data-theme")).toBe(false);
expect(nested.hasAttribute("data-color-scheme")).toBe(false);
expect(nested.hasAttribute("data-density")).toBe(false);
});
});