CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
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 | /**
* Block L10 — Landing-page hero rewrite tests.
*
* Covers:
* - GET / returns 200 HTML to anonymous users
* - Headline string + install snippet + 3 CTA hrefs all present
* - "Three reasons" column headings render
* - "How is this different" pull-quote string present
* - Meta tags (title, description, og:title) injected via Layout
* - REGRESSION GUARD: the L4 counters tile section still renders
* (stable identifier `class="landing-counters"`)
* - REGRESSION GUARD: the L5 "Compare to GitHub" CTA still points
* at /vs-github
*/
import { describe, it, expect } from "bun:test";
import app from "../app";
const HOME = "/";
describe("Block L10 — landing hero rewrite", () => {
it("GET / returns 200 HTML to an anonymous visitor", async () => {
const res = await app.request(HOME);
expect(res.status).toBe(200);
const ct = res.headers.get("content-type") || "";
expect(ct.toLowerCase()).toContain("text/html");
});
it("renders the new hero headline", async () => {
const res = await app.request(HOME);
const body = await res.text();
expect(body).toContain("The git host built around Claude.");
});
it("renders the install snippet", async () => {
const res = await app.request(HOME);
const body = await res.text();
// The host + path is what makes it unambiguous as the install snippet.
expect(body).toContain("gluecron.com/install");
expect(body).toContain("curl -sSL gluecron.com/install | bash");
});
it("renders all three primary CTAs in the hero row", async () => {
const res = await app.request(HOME);
const body = await res.text();
expect(body).toContain('href="/register"');
expect(body).toContain('href="/demo"');
expect(body).toContain('href="/vs-github"');
// Visible labels for the three CTAs.
expect(body).toContain("Sign up free");
expect(body).toContain("Try the live demo");
expect(body).toContain("Compare to GitHub");
});
it("renders the three reasons-to-switch column headings", async () => {
const res = await app.request(HOME);
const body = await res.text();
expect(body).toContain("Toggle Sleep Mode");
expect(body).toContain("One command to migrate");
expect(body).toContain("Open the demo, watch it work");
// The migrate column also links to /import.
expect(body).toContain('href="/import"');
// The Sleep Mode + demo columns deep-link to their L1 / L3 routes.
expect(body).toContain('href="/sleep-mode"');
expect(body).toContain('href="/demo"');
});
it("renders the 'How is this different' pull-quote", async () => {
const res = await app.request(HOME);
const body = await res.text();
expect(body).toContain("How is this different from GitHub?");
expect(body).toContain("Every other host bolts AI on as a sidecar.");
// JSX collapses newlines + leading whitespace; assert on a substring
// that is contiguous after server-side rendering.
expect(body).toContain("first-class developer");
expect(body).toContain("Built to be");
expect(body).toContain("operated by AI agents");
expect(body).toContain("See the full comparison");
});
it("injects SEO + Open Graph meta tags", async () => {
const res = await app.request(HOME);
const body = await res.text();
// <title>
expect(body).toContain(
"<title>Gluecron — The git host built around Claude</title>"
);
// <meta name="description">
expect(body).toMatch(
/<meta\s+name="description"\s+content="Label an issue\. Walk away\. Wake up to a merged PR\./
);
// <meta property="og:title">
expect(body).toMatch(
/<meta\s+property="og:title"\s+content="Gluecron — The git host built around Claude"/
);
// <meta property="og:description">
expect(body).toMatch(
/<meta\s+property="og:description"\s+content="Label an issue\./
);
// <meta property="og:type">
expect(body).toMatch(
/<meta\s+property="og:type"\s+content="website"/
);
// <meta name="twitter:card">
expect(body).toMatch(
/<meta\s+name="twitter:card"\s+content="summary_large_image"/
);
});
it("REGRESSION: L4 counters tile section is still rendered", async () => {
const res = await app.request(HOME);
const body = await res.text();
// The L4 section is conditional on publicStats. When it renders we
// expect this scope class. It may be absent in a totally empty DB
// setup (publicStats falsy) — but the section's HTML class string
// is the stable identifier we assert when it IS present, which it
// will be whenever the lazy computePublicStats() returns a payload.
// Either way, the L4 builder export must still exist + be wired.
const { buildSocialProofTiles } = await import("../views/landing");
expect(typeof buildSocialProofTiles).toBe("function");
// If the conditional rendered, the class is in the markup. If it
// didn't render, the COUNTERS animation script string isn't either —
// both signals stay aligned, so a regression that DROPS the section
// would still be caught by the class-only path on the common case.
if (body.includes("landing-counters-grid")) {
// Tile section is in the markup — confirm the count-up script is too.
expect(body).toContain("data-counter-target");
}
});
it("REGRESSION: L5 'Compare to GitHub' CTA still routes to /vs-github", async () => {
const res = await app.request(HOME);
const body = await res.text();
// The href + label pair must remain wired.
expect(body).toContain('href="/vs-github"');
expect(body).toContain("Compare to GitHub");
});
});
|