返回 CodeWhale
page-meta.test.ts
根目录 / web / lib / page-meta.test.ts
1 import { readFileSync } from "node:fs";
2 import { describe, expect, it } from "vitest";
3 import { locales } from "./i18n/config";
4 import { buildPageMetadata, IDENTITY_PHRASE, SITE_NAME, SITE_URL } from "./page-meta";
5
6 /** hreflang alternates derive from the canonical locale registry. */
7 function expectedLanguages(path: string): Record<string, string> {
8 const languages: Record<string, string> = {};
9 for (const l of locales) {
10 languages[l] = `${SITE_URL}/${l}${path}`;
11 }
12 languages["x-default"] = `${SITE_URL}/en${path}`;
13 return languages;
14 }
15
16 describe("page metadata", () => {
17 it.each([
18 ["en", "/faq", "FAQ · Codewhale", "en_US"],
19 ["zh", "/faq", "常见问题 · Codewhale", "zh_CN"],
20 ["en", "/feed", "Activity · Codewhale", "en_US"],
21 ["zh", "/feed", "动态 · Codewhale", "zh_CN"],
22 ["en", "/roadmap", "Roadmap · Codewhale", "en_US"],
23 ["zh", "/roadmap", "路线图 · Codewhale", "zh_CN"],
24 ["ru", "/faq", "FAQ · Codewhale", "ru_RU"],
25 ["uk", "/faq", "FAQ · Codewhale", "uk_UA"],
26 ["pt-BR", "/install", "Install · Codewhale", "pt_BR"],
27 ])("builds canonical, hreflang, Open Graph, and Twitter fields for %s%s", (locale, path, title, ogLocale) => {
28 const description = `${locale} metadata contract`;
29 const metadata = buildPageMetadata({ path, locale, title, description });
30 const canonical = `${SITE_URL}/${locale}${path}`;
31
32 expect(metadata.alternates).toEqual({
33 canonical,
34 languages: expectedLanguages(path),
35 });
36 expect(metadata.openGraph).toEqual({
37 title,
38 description,
39 url: canonical,
40 siteName: SITE_NAME,
41 type: "website",
42 locale: ogLocale,
43 images: [
44 {
45 url: `${SITE_URL}/opengraph-image`,
46 width: 1200,
47 height: 630,
48 alt: `${SITE_NAME} — ${IDENTITY_PHRASE}`,
49 },
50 ],
51 });
52 expect(metadata.twitter).toEqual({
53 card: "summary_large_image",
54 title,
55 description,
56 images: [`${SITE_URL}/opengraph-image`],
57 });
58 });
59
60 it("emits an hreflang alternate for every routed locale plus x-default", () => {
61 const metadata = buildPageMetadata({
62 path: "/docs",
63 locale: "ja",
64 title: "Docs · Codewhale",
65 description: "hreflang coverage",
66 });
67 const languages = metadata.alternates?.languages as Record<string, string>;
68 for (const l of locales) {
69 expect(languages[l], `missing hreflang for ${l}`).toBe(`${SITE_URL}/${l}/docs`);
70 }
71 expect(languages["x-default"]).toBe(`${SITE_URL}/en/docs`);
72 expect(Object.keys(languages)).toHaveLength(locales.length + 1);
73 });
74
75 it("keeps the previously incomplete indexable routes on the shared helper", () => {
76 for (const [route, path] of [
77 ["faq", "/faq"],
78 ["feed", "/feed"],
79 ["roadmap", "/roadmap"],
80 ]) {
81 const source = readFileSync(
82 new URL(`../app/[locale]/${route}/page.tsx`, import.meta.url),
83 "utf8",
84 );
85 expect(source, route).toContain('import { buildPageMetadata } from "@/lib/page-meta"');
86 expect(source, route).toContain("return buildPageMetadata({");
87 expect(source, route).toContain(`path: "${path}"`);
88 }
89 });
90 });
91
91 lines TYPESCRIPT