WIP! A BB-style forum, on the ATmosphere! We're still working... we'll be back soon when we have something to show off!
node typescript hono htmx atproto
at atb-52-css-token-extraction 93 lines 3.1 kB view raw
1import { describe, it, expect } from "vitest"; 2import { Hono } from "hono"; 3import { ErrorDisplay } from "../error-display.js"; 4import { EmptyState } from "../empty-state.js"; 5import { LoadingState } from "../loading-state.js"; 6 7describe("ErrorDisplay", () => { 8 it("renders message with error-display class", async () => { 9 const app = new Hono().get("/", (c) => 10 c.html(<ErrorDisplay message="Something went wrong" />) 11 ); 12 const res = await app.request("/"); 13 const html = await res.text(); 14 expect(html).toContain('class="error-display"'); 15 expect(html).toContain("Something went wrong"); 16 }); 17 18 it("renders detail when provided", async () => { 19 const app = new Hono().get("/", (c) => 20 c.html(<ErrorDisplay message="Error" detail="Extra context" />) 21 ); 22 const res = await app.request("/"); 23 const html = await res.text(); 24 expect(html).toContain("Extra context"); 25 }); 26 27 it("omits detail element when not provided", async () => { 28 const app = new Hono().get("/", (c) => 29 c.html(<ErrorDisplay message="Error" />) 30 ); 31 const res = await app.request("/"); 32 const html = await res.text(); 33 expect(html).not.toMatch(/<p[^>]*class="error-display__detail"[^>]*>/); 34 }); 35}); 36 37describe("EmptyState", () => { 38 it("renders message with empty-state class", async () => { 39 const app = new Hono().get("/", (c) => 40 c.html(<EmptyState message="No topics yet" />) 41 ); 42 const res = await app.request("/"); 43 const html = await res.text(); 44 expect(html).toContain('class="empty-state"'); 45 expect(html).toContain("No topics yet"); 46 }); 47 48 it("renders action when provided", async () => { 49 const app = new Hono().get("/", (c) => 50 c.html( 51 <EmptyState message="Empty" action={<a href="/new">Create one</a>} /> 52 ) 53 ); 54 const res = await app.request("/"); 55 const html = await res.text(); 56 expect(html).toContain('class="empty-state__action"'); 57 expect(html).toContain("Create one"); 58 }); 59 60 it("omits action element when not provided", async () => { 61 const app = new Hono().get("/", (c) => 62 c.html(<EmptyState message="Empty" />) 63 ); 64 const res = await app.request("/"); 65 const html = await res.text(); 66 expect(html).not.toContain('class="empty-state__action"'); 67 }); 68}); 69 70describe("LoadingState", () => { 71 it("renders with htmx-indicator class", async () => { 72 const app = new Hono().get("/", (c) => c.html(<LoadingState />)); 73 const res = await app.request("/"); 74 const html = await res.text(); 75 expect(html).toContain("htmx-indicator"); 76 }); 77 78 it("renders default loading message", async () => { 79 const app = new Hono().get("/", (c) => c.html(<LoadingState />)); 80 const res = await app.request("/"); 81 const html = await res.text(); 82 expect(html).toContain("Loading"); 83 }); 84 85 it("renders custom message when provided", async () => { 86 const app = new Hono().get("/", (c) => 87 c.html(<LoadingState message="Fetching posts…" />) 88 ); 89 const res = await app.request("/"); 90 const html = await res.text(); 91 expect(html).toContain("Fetching posts"); 92 }); 93});