diff --git a/www/src/lib/seo.test.ts b/www/src/lib/seo.test.ts index b3886a7..1d1e516 100644 --- a/www/src/lib/seo.test.ts +++ b/www/src/lib/seo.test.ts @@ -6,6 +6,7 @@ import { buildWebSiteJsonLd, buildBreadcrumbJsonLd, buildTitle, + buildProductJsonLd, } from "./seo"; describe("seo builders", () => { @@ -70,4 +71,18 @@ describe("seo builders", () => { it("buildTitle appends the brand", () => { expect(buildTitle("PostgreSQL GUI")).toBe("PostgreSQL GUI | Gridline"); }); + + it("buildProductJsonLd returns valid Product schema", () => { + const ld = buildProductJsonLd({ + name: "Gridline", + url: "https://getgridline.app/", + description: "Open-source database GUI", + image: "https://getgridline.app/og.png", + offersPrice: 0, + offersCurrency: "USD", + }); + expect(ld["@type"]).toBe("Product"); + expect(ld.name).toBe("Gridline"); + expect((ld.offers as { price: number }).price).toBe(0); + }); }); diff --git a/www/src/lib/seo.ts b/www/src/lib/seo.ts index e7c9994..7c9fee5 100644 --- a/www/src/lib/seo.ts +++ b/www/src/lib/seo.ts @@ -108,3 +108,26 @@ export function buildBreadcrumbJsonLd( export function buildTitle(primary: string, brand = "Gridline"): string { return `${primary} | ${brand}`; } + +export function buildProductJsonLd(opts: { + name: string; + url: string; + description: string; + image: string; + offersPrice: number; + offersCurrency: string; +}): JsonLd { + return { + "@context": "https://schema.org", + "@type": "Product", + name: opts.name, + url: opts.url, + description: opts.description, + image: opts.image, + offers: { + "@type": "Offer", + price: opts.offersPrice, + priceCurrency: opts.offersCurrency, + }, + }; +}