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

landing-hero.test.ts

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

landing-hero.test.tsBlame119 lines · 1 contributor
5f2e749Claude1/**
2 * Block L10 — Landing-page hero rewrite tests.
3 *
4 * Covers:
5 * - GET / returns 200 HTML to anonymous users
6 * - Headline string + install snippet + 3 CTA hrefs all present
7 * - "Three reasons" column headings render
8 * - "How is this different" pull-quote string present
9 * - Meta tags (title, description, og:title) injected via Layout
10 * - REGRESSION GUARD: the L4 counters tile section still renders
11 * (stable identifier `class="landing-counters"`)
12 * - REGRESSION GUARD: the L5 "Compare to GitHub" CTA still points
13 * at /vs-github
14 */
15import { describe, it, expect } from "bun:test";
16import app from "../app";
17
18const HOME = "/";
19
20describe("Block L10 — landing hero rewrite", () => {
21 it("GET / returns 200 HTML to an anonymous visitor", async () => {
22 const res = await app.request(HOME);
23 expect(res.status).toBe(200);
24 const ct = res.headers.get("content-type") || "";
25 expect(ct.toLowerCase()).toContain("text/html");
26 });
27
5acce80Claude28 it("renders the hero headline", async () => {
5f2e749Claude29 const res = await app.request(HOME);
30 const body = await res.text();
5acce80Claude31 expect(body).toContain("The git host built for");
5f2e749Claude32 });
33
5acce80Claude34 it("renders speed-framing copy in hero lede", async () => {
5f2e749Claude35 const res = await app.request(HOME);
36 const body = await res.text();
5acce80Claude37 expect(body).toContain("Spec to PR in 90 seconds");
5f2e749Claude38 });
39
5acce80Claude40 it("renders primary CTAs in the hero row", async () => {
5f2e749Claude41 const res = await app.request(HOME);
42 const body = await res.text();
43 expect(body).toContain('href="/register"');
5acce80Claude44 // Visible labels for the primary CTAs.
45 expect(body).toContain("Start building");
5f2e749Claude46 });
47
5acce80Claude48 it("renders the register and explore nav links", async () => {
5f2e749Claude49 const res = await app.request(HOME);
50 const body = await res.text();
5acce80Claude51 expect(body).toContain('href="/register"');
52 expect(body).toContain('href="/explore"');
53 expect(body).toContain('href="/pricing"');
5f2e749Claude54 });
55
5acce80Claude56 it("renders the 'The git host' eyebrow or headline copy", async () => {
5f2e749Claude57 const res = await app.request(HOME);
58 const body = await res.text();
5acce80Claude59 expect(body).toContain("AI-native git host");
5f2e749Claude60 });
61
62 it("injects SEO + Open Graph meta tags", async () => {
63 const res = await app.request(HOME);
64 const body = await res.text();
65 // <title>
66 expect(body).toContain(
5acce80Claude67 "<title>Gluecron — The AI-native git host</title>"
5f2e749Claude68 );
69 // <meta name="description">
70 expect(body).toMatch(
5acce80Claude71 /<meta\s+name="description"\s+content="The AI-native git host\. Spec to PR in 90 seconds\./
5f2e749Claude72 );
73 // <meta property="og:title">
74 expect(body).toMatch(
5acce80Claude75 /<meta\s+property="og:title"\s+content="Gluecron — The AI-native git host"/
5f2e749Claude76 );
77 // <meta property="og:description">
78 expect(body).toMatch(
5acce80Claude79 /<meta\s+property="og:description"\s+content="The AI-native git host\./
5f2e749Claude80 );
81 // <meta property="og:type">
82 expect(body).toMatch(
83 /<meta\s+property="og:type"\s+content="website"/
84 );
85 // <meta name="twitter:card">
86 expect(body).toMatch(
87 /<meta\s+name="twitter:card"\s+content="summary_large_image"/
88 );
89 });
90
91 it("REGRESSION: L4 counters tile section is still rendered", async () => {
92 const res = await app.request(HOME);
93 const body = await res.text();
94 // The L4 section is conditional on publicStats. When it renders we
95 // expect this scope class. It may be absent in a totally empty DB
96 // setup (publicStats falsy) — but the section's HTML class string
97 // is the stable identifier we assert when it IS present, which it
98 // will be whenever the lazy computePublicStats() returns a payload.
99 // Either way, the L4 builder export must still exist + be wired.
100 const { buildSocialProofTiles } = await import("../views/landing");
101 expect(typeof buildSocialProofTiles).toBe("function");
102
103 // If the conditional rendered, the class is in the markup. If it
104 // didn't render, the COUNTERS animation script string isn't either —
105 // both signals stay aligned, so a regression that DROPS the section
106 // would still be caught by the class-only path on the common case.
107 if (body.includes("landing-counters-grid")) {
108 // Tile section is in the markup — confirm the count-up script is too.
109 expect(body).toContain("data-counter-target");
110 }
111 });
112
5acce80Claude113 it("REGRESSION: pricing link is present in nav or page body", async () => {
5f2e749Claude114 const res = await app.request(HOME);
115 const body = await res.text();
5acce80Claude116 // Pricing link must remain accessible from the home page.
117 expect(body).toContain('href="/pricing"');
5f2e749Claude118 });
119});