cm_whatsapp_bot_v1/apps/web/src/components/empty-state.test.tsx
yiekheng 3c3a3f57d3 refactor(web): extract EmptyState and reuse on every empty surface
One component now owns the icon / heading / helper / action stack
that the dashboard, accounts list, reminders list, and activity tab
were each rendering inline. The four duplicated 'flex-col items-center
py-12 text-center' Card blocks collapse to one shared surface so the
empty experience reads the same wherever the user lands.

Covered by 4 SSR tests (icon + title + description, omitted helper,
action slot pass-through, centring).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 15:21:51 +08:00

53 lines
1.9 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
import { ActivityIcon } from "lucide-react";
import { EmptyState } from "./empty-state";
describe("EmptyState", () => {
it("renders the icon, title, and description", () => {
const html = renderToStaticMarkup(
<EmptyState
icon={ActivityIcon}
title="No activity yet."
description="Reminder fire events will appear here."
/>,
);
expect(html).toContain("No activity yet.");
expect(html).toContain("Reminder fire events will appear here.");
// The lucide icon component renders an <svg> with the lucide-activity class.
expect(html).toMatch(/<svg[^>]*lucide-activity/);
});
it("omits the description when it isn't passed", () => {
const html = renderToStaticMarkup(
<EmptyState icon={ActivityIcon} title="No archived runs." />,
);
expect(html).toContain("No archived runs.");
// No second <p> element for the helper text — the only <p> is the title.
expect((html.match(/<p\b/g) ?? []).length).toBe(1);
});
it("renders the action slot when provided", () => {
const html = renderToStaticMarkup(
<EmptyState
icon={ActivityIcon}
title="No reminders yet."
action={<button data-testid="cta">Schedule one</button>}
/>,
);
expect(html).toContain('data-testid="cta"');
expect(html).toContain("Schedule one");
});
it("centres the layout (icon → text → action stack)", () => {
const html = renderToStaticMarkup(
<EmptyState icon={ActivityIcon} title="x" action={<span>cta</span>} />,
);
// The CardContent uses flex-col items-center text-center for the
// canonical empty state layout. Lock that in so future tweaks
// can't accidentally drop the centring.
expect(html).toMatch(/flex-col items-center/);
expect(html).toMatch(/text-center/);
});
});