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

pricing-page.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.

pricing-page.test.tsBlame104 lines · 1 contributor
5f2e749Claude1/**
2 * Block L8 — public /pricing page tests.
3 *
4 * Anonymous-safe route. Verifies:
5 * - GET /pricing returns 200 HTML to a logged-out visitor
6 * - All four plan names (Free, Pro, Team, Enterprise) render
7 * - The "what you get on free" block contains the AI features
8 * - All five FAQ questions are present verbatim
9 * - CTA links resolve to /register?next=... or /settings/billing
10 * - The self-host column mentions the curl install line
11 */
12
13import { describe, it, expect } from "bun:test";
14import app from "../app";
15import { FALLBACK_PLANS } from "../lib/billing";
16
17describe("L8 — /pricing public page", () => {
18 it("returns 200 HTML to an anonymous visitor", async () => {
19 const res = await app.request("/pricing");
20 expect(res.status).toBe(200);
21 const ct = res.headers.get("content-type") || "";
22 expect(ct.toLowerCase()).toContain("text/html");
23 });
24
25 it("renders all four plan names (Free, Pro, Team, Enterprise)", async () => {
26 const res = await app.request("/pricing");
27 const body = await res.text();
28 // FALLBACK_PLANS guarantees the four names exist regardless of whether
29 // the DB seeds are loaded — listPlans() falls back to these.
30 for (const slug of Object.keys(FALLBACK_PLANS)) {
31 const name = FALLBACK_PLANS[slug].name;
32 expect(body).toContain(name);
33 }
34 });
35
36 it("the free-tier block lists at least 6 of the included AI features", async () => {
37 const res = await app.request("/pricing");
38 const body = await res.text();
39 const features = [
40 "Unlimited public repos",
41 "AI code review on every PR",
42 "AI auto-merge",
43 "ai:build label",
44 "Sleep Mode digest",
45 "AI hours saved counter",
46 "MCP server access",
47 "Claude Code skill bundle",
48 "One-command install",
49 "GitHub OIDC sign-in",
50 ];
51 const present = features.filter((f) => body.includes(f));
52 expect(present.length).toBeGreaterThanOrEqual(6);
53 });
54
55 it("FAQ contains all five required questions", async () => {
56 const res = await app.request("/pricing");
57 const body = await res.text();
58 expect(body).toContain("Is it really free? What's the catch?");
59 expect(body).toContain(
60 "Do I need to bring my own Anthropic API key on the free tier?"
61 );
62 expect(body).toContain("What happens when I exceed my plan's quota?");
63 expect(body).toContain("Can I migrate from GitHub for free?");
64 expect(body).toContain("Does the free tier include private repos?");
65 });
66
67 it("CTAs route anonymous users through /register?next=/settings/billing", async () => {
68 const res = await app.request("/pricing");
69 const body = await res.text();
70 // At least one register-funnel CTA must exist.
71 expect(body).toMatch(/href="\/register(\?next=\/settings\/billing[^"]*)?"/);
72 // And the page must mention /settings/billing somewhere as the
73 // destination after sign-up.
74 expect(body).toContain("/settings/billing");
75 });
76
77 it("self-host column mentions `curl gluecron.com/install`", async () => {
78 const res = await app.request("/pricing");
79 const body = await res.text();
80 expect(body).toContain("curl gluecron.com/install");
81 });
82
83 it("does not require authentication (no redirect)", async () => {
84 const res = await app.request("/pricing");
85 expect(res.status).toBe(200);
86 expect(res.status).not.toBe(302);
87 expect(res.status).not.toBe(401);
88 });
89
13ac035Claude90 it("hero copy reflects the 2026 polish — bundle-math positioning", async () => {
91 // 2026-05-16 polish — pricing hero copy rewritten to emphasise the
92 // "GitHub bundle math" positioning instead of the original
93 // "Free for the AI-curious" line. The new hero leads with "One
94 // subscription. Replaces three on GitHub." and the sub-copy lands
95 // the $89/user/mo GitHub-stack comparison.
5f2e749Claude96 const res = await app.request("/pricing");
97 const body = await res.text();
13ac035Claude98 expect(body).toContain("One subscription.");
99 expect(body).toContain("Replaces three on GitHub.");
100 // The "vs GitHub bundle math" comparison block must render.
101 expect(body).toContain("Bundle math vs GitHub");
102 expect(body).toContain("$89");
5f2e749Claude103 });
104});