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.
| 5f2e749 | 1 | /** |
| 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 | */ | |
| 15 | import { describe, it, expect } from "bun:test"; | |
| 16 | import app from "../app"; | |
| 17 | ||
| 18 | const HOME = "/"; | |
| 19 | ||
| 20 | describe("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 | ||
| 5acce80 | 28 | it("renders the hero headline", async () => { |
| 5f2e749 | 29 | const res = await app.request(HOME); |
| 30 | const body = await res.text(); | |
| f8e5fea | 31 | expect(body).toContain("The platform"); |
| 5f2e749 | 32 | }); |
| 33 | ||
| 5acce80 | 34 | it("renders speed-framing copy in hero lede", async () => { |
| 5f2e749 | 35 | const res = await app.request(HOME); |
| 36 | const body = await res.text(); | |
| f8e5fea | 37 | expect(body).toContain("under 90 seconds"); |
| 5f2e749 | 38 | }); |
| 39 | ||
| 5acce80 | 40 | it("renders primary CTAs in the hero row", async () => { |
| 5f2e749 | 41 | const res = await app.request(HOME); |
| 42 | const body = await res.text(); | |
| 43 | expect(body).toContain('href="/register"'); | |
| 5acce80 | 44 | // Visible labels for the primary CTAs. |
| 45 | expect(body).toContain("Start building"); | |
| 5f2e749 | 46 | }); |
| 47 | ||
| 5acce80 | 48 | it("renders the register and explore nav links", async () => { |
| 5f2e749 | 49 | const res = await app.request(HOME); |
| 50 | const body = await res.text(); | |
| 5acce80 | 51 | expect(body).toContain('href="/register"'); |
| 52 | expect(body).toContain('href="/explore"'); | |
| 53 | expect(body).toContain('href="/pricing"'); | |
| 5f2e749 | 54 | }); |
| 55 | ||
| 5acce80 | 56 | it("renders the 'The git host' eyebrow or headline copy", async () => { |
| 5f2e749 | 57 | const res = await app.request(HOME); |
| 58 | const body = await res.text(); | |
| f8e5fea | 59 | expect(body).toContain("AI-native git platform"); |
| 5f2e749 | 60 | }); |
| 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( | |
| f8e5fea | 67 | "<title>Gluecron — The AI-native git platform</title>" |
| 5f2e749 | 68 | ); |
| 69 | // <meta name="description"> | |
| 70 | expect(body).toMatch( | |
| f8e5fea | 71 | /<meta\s+name="description"\s+content="The git platform that does the work\./ |
| 5f2e749 | 72 | ); |
| 73 | // <meta property="og:title"> | |
| 74 | expect(body).toMatch( | |
| f8e5fea | 75 | /<meta\s+property="og:title"\s+content="Gluecron — The AI-native git platform"/ |
| 5f2e749 | 76 | ); |
| 77 | // <meta property="og:description"> | |
| 78 | expect(body).toMatch( | |
| f8e5fea | 79 | /<meta\s+property="og:description"\s+content="The git platform that does the work\./ |
| 5f2e749 | 80 | ); |
| 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 | ||
| 5acce80 | 113 | it("REGRESSION: pricing link is present in nav or page body", async () => { |
| 5f2e749 | 114 | const res = await app.request(HOME); |
| 115 | const body = await res.text(); | |
| 5acce80 | 116 | // Pricing link must remain accessible from the home page. |
| 117 | expect(body).toContain('href="/pricing"'); | |
| 5f2e749 | 118 | }); |
| 119 | }); |