import { afterEach, describe, expect, it } from "vitest"; import metadataMessages from "../../messages/en/metadata.json"; import { buildRobots, PRODUCTION_SITE_ORIGIN, resolveRequestHost, routeMetadata, sitemapUrl, } from "../../lib/siteMetadata"; const ORIGINAL_SITE_INDEXING = process.env.SITE_INDEXING; const ORIGINAL_APP_ORIGIN = process.env.CLOUDRON_APP_ORIGIN; afterEach(() => { if (ORIGINAL_SITE_INDEXING === undefined) { delete process.env.SITE_INDEXING; } else { process.env.SITE_INDEXING = ORIGINAL_SITE_INDEXING; } if (ORIGINAL_APP_ORIGIN === undefined) { delete process.env.CLOUDRON_APP_ORIGIN; } else { process.env.CLOUDRON_APP_ORIGIN = ORIGINAL_APP_ORIGIN; } }); describe("routeMetadata", () => { it("sets a path canonical, stripping trailing slashes and query strings", () => { expect(routeMetadata("/about").alternates?.canonical).toBe("/about"); expect(routeMetadata("/about/").alternates?.canonical).toBe("/about"); expect(routeMetadata("about").alternates?.canonical).toBe("/about"); expect(routeMetadata("/").alternates?.canonical).toBe("/"); expect(routeMetadata("/blog/post?x=1#top").alternates?.canonical).toBe( "/blog/post", ); }); it("keeps caller metadata while overriding a parent canonical", () => { expect( routeMetadata("/learn", { title: "Learn", description: "Copy" }), ).toEqual({ title: "Learn", description: "Copy", alternates: { canonical: "/learn" }, }); }); }); describe("sitemapUrl", () => { it("resolves paths against the production origin", () => { expect(sitemapUrl("/")).toBe(PRODUCTION_SITE_ORIGIN); expect(sitemapUrl("/about")).toBe(`${PRODUCTION_SITE_ORIGIN}/about`); }); }); describe("resolveRequestHost", () => { it("prefers x-forwarded-host over Host", () => { expect( resolveRequestHost("staging.communityrule.info", "0.0.0.0:3000"), ).toBe("staging.communityrule.info"); }); it("uses CLOUDRON_APP_ORIGIN when Host is the standalone bind address", () => { process.env.CLOUDRON_APP_ORIGIN = "https://staging.communityrule.info"; expect(resolveRequestHost(null, "0.0.0.0:3000")).toBe( "staging.communityrule.info", ); }); }); describe("buildRobots", () => { it("disallows crawlers on staging and unknown hosts", () => { expect(buildRobots("staging.communityrule.info")).toEqual({ rules: { userAgent: "*", disallow: "/" }, }); expect(buildRobots("localhost:3000")).toEqual({ rules: { userAgent: "*", disallow: "/" }, }); }); it("allows production hosts and advertises the sitemap", () => { expect(buildRobots("communityrule.info")).toEqual({ rules: { userAgent: "*", allow: "/" }, sitemap: `${PRODUCTION_SITE_ORIGIN}/sitemap.xml`, }); expect(buildRobots("communityrule.com")).toEqual({ rules: { userAgent: "*", allow: "/" }, sitemap: `${PRODUCTION_SITE_ORIGIN}/sitemap.xml`, }); }); it("honors SITE_INDEXING overrides", () => { process.env.SITE_INDEXING = "false"; expect(buildRobots("communityrule.info").rules).toEqual({ userAgent: "*", disallow: "/", }); process.env.SITE_INDEXING = "true"; expect(buildRobots("staging.communityrule.info").sitemap).toBe( `${PRODUCTION_SITE_ORIGIN}/sitemap.xml`, ); }); }); describe("page title segments", () => { it("uses a shared em-dash template and does not bake a site suffix into page titles", () => { expect(metadataMessages.titleTemplate).toBe("%s — CommunityRule"); const suffixes = [ " — CommunityRule", " · CommunityRule", " - CommunityRule", ]; const titles: string[] = []; const walk = (value: unknown) => { if (value && typeof value === "object") { for (const [key, child] of Object.entries( value as Record, )) { if (key === "title" && typeof child === "string") { titles.push(child); } else { walk(child); } } } }; walk(metadataMessages); for (const title of titles) { if (title === metadataMessages.home.title) continue; for (const suffix of suffixes) { expect(title.endsWith(suffix)).toBe(false); } } }); });