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
claude/adoring-hopper-5x74bqclaude/affectionate-feynman-ykrf1hclaude/architecture-audit-design-wxprenclaude/build-status-update-3MXsfclaude/charming-meitner-mllb5rclaude/compare-gate-gluecron-s4mFQclaude/confident-faraday-tikcwbclaude/continue-work-XMTlIclaude/crontech-gluecron-deploy-7MIECclaude/crontech-platform-setup-SeKfwclaude/design-2026claude/ecstatic-ptolemy-jMdigclaude/enhance-github-integration-QNHdGclaude/fix-aa-loop-issue-PonMQclaude/fix-actions-and-processclaude/fix-desktop-errors-XqoW8claude/fix-red-workflowsclaude/fix-website-access-6FKJNclaude/gatetest-integration-hardeningclaude/github-audit-improvements-bDFr9claude/gluecron-launch-status-FoMRlclaude/hopeful-lamport-olfCTclaude/issue-to-pr-and-protectionsclaude/jolly-heisenberg-2sg1Qclaude/launch-preparation-QmTb6claude/new-session-xk1l7claude/plan-platform-architecture-kkN4yclaude/platform-analysis-roadmap-1nUGLclaude/platform-launch-assessment-8dWV8claude/polish-platform-release-AeDrUclaude/resume-previous-work-KzyLwclaude/review-crontech-handoff-qYEVqclaude/review-project-completeness-lHhS2claude/review-readme-docs-ulqPKclaude/serene-edison-rj87weclaude/setup-multi-repo-dev-BCwNQclaude/ship-fixes-and-tests-Jvz1cclaude/site-audit-competitive-pctlwgclaude/site-migration-vercel-XstpKclaude/standalone-product-repos-XHFTDcopilot/feat-smart-empty-states-keyboard-first-enhancementcopilot/feat-smart-morning-digest-review-context-restorecopilot/fix-and-process-workflowscopilot/update-ai-powered-code-reviewfeat/debt-mapfeat/push-policy-codeowners-hardeningfeat/smart-digest-contextfeat/stage-impactfeat/t1-secret-migrationfeat/u-polishfeat/w-self-hostfeat/w2-claude-configfix/agent-journey-orphan-sweepgatetest/auto-fix-1776586424172gatetest/auto-fix-1776586534814gatetest/auto-fix-1776590685143gatetest/auto-fix-1776590808199mainops/redeploy-retriggerstyle/dxt-cta-themeworktree-agent-a3377aad30d55da26worktree-agent-a7ef607b7ee1d6c74
system-states.test.ts10.3 KB · 283 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/**
 * BLOCK O2 — Every system state polished.
 *
 * Covers the four polished surfaces:
 *   1. Error pages (404 / 500 / 403)
 *   2. Empty state component
 *   3. Skeleton component
 *   4. Form validation script
 *
 * No mock pollution — each test renders the real component to JSX
 * string output (or hits the real Hono router) and asserts on the
 * rendered HTML.
 */

import { describe, it, expect } from "bun:test";
import app from "../app";
import {
  ErrorPage,
  NotFoundPage,
  ServerErrorPage,
  ForbiddenPage,
  renderStandaloneErrorPage,
} from "../views/error-page";
import { EmptyState } from "../views/empty-state";
import {
  Skeleton,
  SkeletonList,
  SkeletonRepoRow,
} from "../views/skeleton";
import {
  formValidationScript,
  formValidationCss,
  FormValidationAssets,
} from "../views/form-validation-js";

// Render a JSX element to its HTML string via Hono's JSX runtime.
async function renderJsx(node: any): Promise<string> {
  if (node && typeof node === "object" && "toString" in node) {
    const s = await Promise.resolve(node.toString());
    return typeof s === "string" ? s : String(s);
  }
  return String(node);
}

describe("BLOCK O2 — Error pages", () => {
  it("GET unknown deep path returns the polished 404 page via app.notFound", async () => {
    // A path with 4+ segments doesn't match any defined route — falls
    // through to app.notFound. (`/:owner` would otherwise swallow a
    // single-segment 404 with a 200 user-profile shell on DB miss.)
    const res = await app.request("/__o2_unknown__/a/b/c/d");
    expect(res.status).toBe(404);
    const body = await res.text();
    // Big "404" gradient text
    expect(body).toContain("data-error-code=\"404\"");
    expect(body).toContain(">404<");
    expect(body).toContain("gradient-text");
    // Two CTAs (primary + secondary)
    expect(body).toMatch(/data-error-cta="primary"/);
    expect(body).toMatch(/data-error-cta="secondary"/);
    expect(body).toContain("Go home");
    expect(body).toContain("Status page");
    // Subhead/body
    expect(body).toContain("Not found");
  });

  it("ServerErrorPage component renders the 500 + request ID line", async () => {
    const node = ServerErrorPage({
      user: null,
      requestId: "req-test-abc-123",
    } as any);
    const body = await renderJsx(node);
    expect(body).toContain(">500<");
    expect(body).toContain("Server error");
    expect(body).toContain("Request ID:");
    expect(body).toContain("req-test-abc-123");
    // Two CTAs
    expect(body).toMatch(/data-error-cta="primary"/);
    expect(body).toMatch(/data-error-cta="secondary"/);
  });

  it("ForbiddenPage component renders the polished 403", async () => {
    const node = ForbiddenPage({ user: null } as any);
    const body = await renderJsx(node);
    expect(body).toContain(">403<");
    expect(body).toContain("Forbidden");
    expect(body).toContain("Admin access required");
    // Signed-out path → "Sign in" suggestion
    expect(body).toContain("Sign in");
  });

  it("ForbiddenPage when signed in offers 'sign in as different user'", async () => {
    const fakeUser: any = { id: "u1", username: "alice" };
    const node = ForbiddenPage({ user: fakeUser } as any);
    const body = await renderJsx(node);
    expect(body).toContain("Sign in as a different user");
    expect(body).toContain("/logout");
  });

  it("renderStandaloneErrorPage returns a complete <!doctype html> document", () => {
    const html = renderStandaloneErrorPage({
      code: "403",
      eyebrow: "Forbidden",
      title: "Admin access required.",
      body: "Body copy.",
      signedIn: true,
    });
    expect(html.toLowerCase()).toContain("<!doctype html>");
    expect(html).toContain("data-error-code=\"403\"");
    expect(html).toContain("Admin access required.");
    expect(html).toContain("Body copy.");
    // Self-contained — has its own <style> + gradient CSS for the code.
    expect(html).toContain("background-clip:text");
    // Signed-in CTA
    expect(html).toContain("Sign in as a different user");
  });

  it("ErrorPage component honours a custom Request ID + trace", async () => {
    const node = ErrorPage({
      code: "500",
      eyebrow: "Server error",
      title: "Boom.",
      body: "Body.",
      requestId: "req-xyz",
      trace: "Error: synthetic\n  at test",
    } as any);
    const body = await renderJsx(node);
    expect(body).toContain("req-xyz");
    expect(body).toContain("error-page-trace");
    expect(body).toContain("Error: synthetic");
  });
});

describe("BLOCK O2 — EmptyState component", () => {
  it("renders title, body and both CTAs", async () => {
    const node = EmptyState({
      icon: "🐛",
      title: "No issues yet",
      body: "When you or your team file issues, they'll appear here.",
      primaryCta: { href: "/new", label: "Open the first issue" },
      secondaryCta: { href: "/closed", label: "View closed" },
    } as any);
    const body = await renderJsx(node);
    expect(body).toContain("No issues yet");
    expect(body).toContain("When you or your team file issues");
    expect(body).toContain("Open the first issue");
    expect(body).toContain("View closed");
    expect(body).toContain("/new");
    expect(body).toContain("/closed");
    // The default icon SVG isn't used here because we passed an emoji.
    expect(body).toContain("🐛");
  });

  it("uses the default gradient SVG when icon is omitted", async () => {
    const node = EmptyState({
      title: "Nothing here",
      body: "Body.",
    } as any);
    const body = await renderJsx(node);
    // SVG gradient block is inlined.
    expect(body).toContain("linearGradient");
    expect(body).toContain("#5b6ee8");
    expect(body).toContain("#5f8fa0");
  });

  it("renders without CTAs when none are provided", async () => {
    const node = EmptyState({
      title: "Quiet",
      body: "Nothing to do.",
    } as any);
    const body = await renderJsx(node);
    expect(body).toContain("Quiet");
    // No <a class="btn"> markers
    expect(body).not.toContain('class="btn btn-primary"');
  });
});

describe("BLOCK O2 — Skeleton component", () => {
  it("renders the requested number of bars", async () => {
    const node = Skeleton({ count: 5, height: "14px" } as any);
    const body = await renderJsx(node);
    // 5 skeleton bars
    const matches = body.match(/class="skeleton-bar"/g) || [];
    expect(matches.length).toBe(5);
    // height inline
    expect(body).toContain("height:14px");
  });

  it("defaults to one bar", async () => {
    const node = Skeleton({} as any);
    const body = await renderJsx(node);
    const matches = body.match(/class="skeleton-bar"/g) || [];
    expect(matches.length).toBe(1);
  });

  it("SkeletonRepoRow renders an avatar circle and two text bars", async () => {
    const node = SkeletonRepoRow({} as any);
    const body = await renderJsx(node);
    expect(body).toContain("border-radius:50%");
    const matches = body.match(/class="skeleton-bar"/g) || [];
    expect(matches.length).toBe(3); // avatar + 2 text bars
  });

  it("SkeletonList renders N rep rows with aria-busy", async () => {
    const node = SkeletonList({ count: 3 } as any);
    const body = await renderJsx(node);
    const rows = body.match(/class="skeleton-repo-row"/g) || [];
    expect(rows.length).toBe(3);
    expect(body).toContain('aria-busy="true"');
    expect(body).toContain('role="status"');
  });

  it("emits the @keyframes skeleton-shimmer animation", async () => {
    const node = Skeleton({} as any);
    const body = await renderJsx(node);
    expect(body).toContain("@keyframes skeleton-shimmer");
    expect(body).toContain("background-position: 200% 0");
  });
});

describe("BLOCK O2 — formValidationScript", () => {
  it("is well-formed JS (parseable by the engine)", () => {
    // `new Function` throws SyntaxError on malformed JS.
    expect(() => {
      new Function(formValidationScript);
    }).not.toThrow();
  });

  it("contains the expected event handlers", () => {
    expect(formValidationScript).toContain('addEventListener("blur"');
    expect(formValidationScript).toContain('addEventListener("input"');
    expect(formValidationScript).toContain("__gluecronFormValidationMounted");
    // Guards against double-mount.
    expect(formValidationScript).toContain("if (window.__gluecronFormValidationMounted) return");
  });

  it("validates required / email / pattern / minlength / maxlength", () => {
    expect(formValidationScript).toContain('hasAttribute("required")');
    expect(formValidationScript).toContain('el.type === "email"');
    expect(formValidationScript).toContain('getAttribute("pattern")');
    expect(formValidationScript).toContain('getAttribute("minlength")');
    expect(formValidationScript).toContain('getAttribute("maxlength")');
  });

  it("respects data-validation-message overrides", () => {
    expect(formValidationScript).toContain('getAttribute("data-validation-message")');
    expect(formValidationScript).toContain('getAttribute("data-validation-required")');
  });

  it("formValidationCss exposes the .input-valid / .input-invalid / .field-error styles", () => {
    expect(formValidationCss).toContain(".input-invalid");
    expect(formValidationCss).toContain(".input-valid");
    expect(formValidationCss).toContain(".field-error");
    // SVG checkmark for green-state confirmation
    expect(formValidationCss).toContain("data:image/svg+xml");
  });

  it("FormValidationAssets emits both style and script tags", async () => {
    const node = FormValidationAssets({} as any);
    const body = await renderJsx(node);
    expect(body).toContain("<style");
    expect(body).toContain("<script");
    expect(body).toContain("input-invalid");
    expect(body).toContain("__gluecronFormValidationMounted");
  });
});

describe("BLOCK O2 — NotFoundPage component (unit, no HTTP)", () => {
  it("renders the 404 number, both CTAs, and the suggestion list", async () => {
    const node = NotFoundPage({ user: null, method: "GET", path: "/foo" } as any);
    const body = await renderJsx(node);
    expect(body).toContain(">404<");
    expect(body).toContain("Not found");
    expect(body).toContain("We can&#39;t find that page");
    // Two CTAs
    expect(body).toMatch(/data-error-cta="primary"/);
    expect(body).toMatch(/data-error-cta="secondary"/);
    // Method/path meta
    expect(body).toContain("GET /foo");
    // Suggestion list
    expect(body).toContain("Explore public repositories");
    expect(body).toContain("Read the quickstart guide");
  });
});