import { describe, expect, test } from "bun:test"; import { readFileSync } from "node:fs"; import { join } from "node:path"; import { accountOgPath, featureOgPath, repoOgPath, siteOgPath } from "~/lib/og"; import { seo } from "./seo"; const ORIGIN = "https://gitfor.dev"; function tags(input: Parameters[0]) { const out = seo(input); const byProperty = (key: string) => out.find((tag) => ("property" in tag && tag.property === key) || ("name" in tag && tag.name === key)); return { out, get: (key: string) => (byProperty(key) as { content?: string } | undefined)?.content }; } function expectSocial(result: ReturnType, image: string) { expect(result.get("og:image")).toBe(`${ORIGIN}${image}`); expect(result.get("twitter:image")).toBe(`${ORIGIN}${image}`); expect(result.get("twitter:card")).toBe("summary_large_image"); expect(result.get("og:site_name")).toBe("gitfor"); expect(result.get("og:image:width")).toBe("1200"); expect(result.get("og:image:height")).toBe("630"); expect(result.get("og:image:alt")).toBeTruthy(); expect(result.out.some((tag) => "name" in tag && tag.name?.startsWith("og:"))).toBe(false); } const repo = { owner: "alice", name: "tool", description: "A tool", stars: 1, forks: 0, openIssues: 0, openPulls: 0, avatarKey: null, latestRelease: null, topics: [], languages: [], }; describe("seo tags", () => { test("repository pages", () => { const image = repoOgPath(repo); const result = tags({ origin: ORIGIN, path: "/alice/tool", title: "alice/tool", description: "A tool", image }); expectSocial(result, image); expect(result.get("og:url")).toBe(`${ORIGIN}/alice/tool`); expect(result.get("og:title")).toBe("alice/tool"); expect(result.get("og:description")).toBe("A tool"); }); test("private repositories share only the generic title and card", () => { const result = tags({ origin: ORIGIN, title: "gitfor", documentTitle: "alice/secret · gitfor" }); expectSocial(result, siteOgPath()); expect(result.get("og:title")).toBe("gitfor"); expect(result.get("og:url")).toBeUndefined(); expect(result.get("og:description")).toBeUndefined(); expect(JSON.stringify(result.out.filter((tag) => !("title" in tag)))).not.toContain("secret"); }); test("profile and org pages", () => { const image = accountOgPath({ name: "acme", kind: "org", displayName: null, bio: null, avatarKey: null, publicRepos: 3, }); const result = tags({ origin: ORIGIN, path: "/acme", title: "acme · gitfor", image }); expectSocial(result, image); expect(result.get("og:url")).toBe(`${ORIGIN}/acme`); }); test("landing and legal pages use the site card", () => { for (const path of ["/", "/terms", "/privacy"]) { const result = tags({ origin: ORIGIN, path, title: "gitfor" }); expectSocial(result, siteOgPath()); expect(result.get("og:url")).toBe(`${ORIGIN}${path}`); } }); test("feature pages", () => { const image = featureOgPath("review", "Code review", "Review code."); expectSocial(tags({ origin: ORIGIN, path: "/features/review", title: "Code review", image }), image); }); }); describe("routes wire social tags", () => { const routes = join(import.meta.dir, "../routes"); const files = [ "index.tsx", "$owner.$repo.tsx", "$owner.index.tsx", "features.$slug.tsx", "features.index.tsx", "terms.tsx", "privacy.tsx", "cookies.tsx", "dmca.tsx", "acceptable-use.tsx", "security.tsx", ]; test.each(files)("%s passes the origin to seo()", (file) => { const source = readFileSync(join(routes, file), "utf8"); expect(source).toContain("seo({"); expect(source).toContain("origin: match.context.publicUrl"); }); });