Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

visual-coherence.test.tsx

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

visual-coherence.test.tsxBlame189 lines · 1 contributor
c63b860Claude1/**
2 * Block O3 — visual coherence tests.
3 *
4 * Asserts the design-token + component contract introduced by the O3
5 * pass.
6 *
74a8784Claude7 * 2026-06-10: token assertions moved from "/" to "/help" — the home
8 * route now serves the self-contained Landing2030Page without the
9 * master Layout CSS.
10 *
c63b860Claude11 * NOTE: This test runs WITHOUT mock pollution — no `mock.module()`,
12 * no shared global state, no DB. It only hits in-process HTTP routes
13 * via `app.request()` and renders the canonical components directly
14 * to strings via hono/jsx's built-in stringifier.
15 */
16
17import { describe, it, expect } from "bun:test";
18import app from "../app";
19import { Card } from "../views/ui";
20import { Layout } from "../views/layout";
21
22// hono/jsx exposes a stringifier on every node.
23async function renderToString(node: any): Promise<string> {
24 if (node && typeof node.toString === "function") {
25 return String(await node.toString());
26 }
27 return String(node);
28}
29
30describe("O3 — design token aliases in master CSS", () => {
31 it("layout.tsx exposes the --space-* alias scale", async () => {
74a8784Claude32 const res = await app.request("/help");
c63b860Claude33 const body = await res.text();
34 expect(body).toContain("--space-1:");
35 expect(body).toContain("--space-2:");
36 expect(body).toContain("--space-3:");
37 expect(body).toContain("--space-4:");
38 expect(body).toContain("--space-6:");
39 expect(body).toContain("--space-8:");
40 });
41
42 it("layout.tsx exposes the --radius-* alias scale", async () => {
74a8784Claude43 const res = await app.request("/help");
c63b860Claude44 const body = await res.text();
45 expect(body).toContain("--radius-sm:");
46 expect(body).toContain("--radius-md:");
47 expect(body).toContain("--radius-lg:");
48 expect(body).toContain("--radius-full:");
49 });
50
51 it("layout.tsx exposes the --font-size-* alias scale", async () => {
74a8784Claude52 const res = await app.request("/help");
c63b860Claude53 const body = await res.text();
54 expect(body).toContain("--font-size-xs:");
55 expect(body).toContain("--font-size-sm:");
56 expect(body).toContain("--font-size-base:");
57 expect(body).toContain("--font-size-lg:");
58 expect(body).toContain("--font-size-xl:");
59 });
60
61 it("layout.tsx exposes the --leading-* aliases", async () => {
74a8784Claude62 const res = await app.request("/help");
c63b860Claude63 const body = await res.text();
64 expect(body).toContain("--leading-tight:");
65 expect(body).toContain("--leading-normal:");
66 expect(body).toContain("--leading-loose:");
67 });
68
69 it("layout.tsx exposes the --z-* index scale", async () => {
74a8784Claude70 const res = await app.request("/help");
c63b860Claude71 const body = await res.text();
72 expect(body).toContain("--z-nav:");
73 expect(body).toContain("--z-modal:");
74 expect(body).toContain("--z-toast:");
75 });
76
77 it("ships the notice / email-preview / code-block utility classes", async () => {
74a8784Claude78 const res = await app.request("/help");
c63b860Claude79 const body = await res.text();
80 expect(body).toContain(".notice");
81 expect(body).toContain(".notice-warn");
82 expect(body).toContain(".email-preview");
83 expect(body).toContain(".code-block");
84 });
85});
86
87describe("O3 — site-wide footer renders on every page", () => {
88 // /explore queries the DB which is not available in the unit-test
89 // process. Footer presence is well-covered by the other four pages.
90 const PAGES = ["/", "/help", "/status", "/pricing"];
91
92 for (const path of PAGES) {
93 it(`GET ${path} includes the canonical footer`, async () => {
94 const res = await app.request(path);
95 expect(res.status).toBeLessThan(500);
96 const body = await res.text();
97 expect(body).toContain("<footer");
98 expect(body.toLowerCase()).toContain("gluecron");
99 });
100 }
101});
102
103describe("O3 — Card component padding + variant props", () => {
104 it("renders without props (legacy default)", async () => {
105 const html = await renderToString(<Card>hello</Card>);
106 expect(html).toContain('class="card"');
107 expect(html).toContain("hello");
108 });
109
110 it('supports padding="none"', async () => {
111 const html = await renderToString(<Card padding="none">x</Card>);
112 expect(html).toContain("card-p-none");
113 });
114
115 it('supports padding="sm"', async () => {
116 const html = await renderToString(<Card padding="sm">x</Card>);
117 expect(html).toContain("card-p-sm");
118 });
119
120 it('supports padding="md"', async () => {
121 const html = await renderToString(<Card padding="md">x</Card>);
122 expect(html).toContain("card-p-md");
123 });
124
125 it('supports padding="lg"', async () => {
126 const html = await renderToString(<Card padding="lg">x</Card>);
127 expect(html).toContain("card-p-lg");
128 });
129
130 it('supports variant="elevated"', async () => {
131 const html = await renderToString(<Card variant="elevated">x</Card>);
132 expect(html).toContain("card-elevated");
133 });
134
135 it('supports variant="gradient"', async () => {
136 const html = await renderToString(<Card variant="gradient">x</Card>);
137 expect(html).toContain("card-gradient");
138 });
139
140 it("composes padding and variant together", async () => {
141 const html = await renderToString(
142 <Card padding="lg" variant="elevated">x</Card>
143 );
144 expect(html).toContain("card-p-lg");
145 expect(html).toContain("card-elevated");
146 });
147});
148
149describe("O3 — flag-gated footer banner", () => {
150 it("does NOT render the .footer-banner stripe when siteBannerText is empty", async () => {
151 const html = await renderToString(
152 <Layout title="t">
153 <p>body</p>
154 </Layout>
155 );
156 expect(html).not.toContain('class="footer-banner');
157 });
158
159 it("renders the .footer-banner stripe when siteBannerText is non-empty", async () => {
160 const html = await renderToString(
161 <Layout title="t" siteBannerText="Scheduled maintenance tonight">
162 <p>body</p>
163 </Layout>
164 );
165 expect(html).toContain("footer-banner");
166 expect(html).toContain("Scheduled maintenance tonight");
167 });
168
169 it("honours the siteBannerLevel modifier", async () => {
170 const html = await renderToString(
171 <Layout title="t" siteBannerText="x" siteBannerLevel="warn">
172 <p>body</p>
173 </Layout>
174 );
175 expect(html).toContain("footer-banner-warn");
176 });
177});
178
179describe("O3 — no critical page leaks a banner stripe by default", () => {
180 const PAGES = ["/", "/help", "/pricing"];
181
182 for (const path of PAGES) {
183 it(`GET ${path} does not render the footer-banner stripe by default`, async () => {
184 const res = await app.request(path);
185 const body = await res.text();
186 expect(body).not.toContain('class="footer-banner');
187 });
188 }
189});