Commit f5d33eed by PLN (Algolia)

blog: a folder with neither meta.md nor a language file is not a post —…

blog: a folder with neither meta.md nor a language file is not a post — seed-note folders mint no slug or route; tests own their single-language fixtures
parent c2311673
...@@ -28,7 +28,16 @@ export const BLOG_LANGS = ['fr', 'en']; ...@@ -28,7 +28,16 @@ export const BLOG_LANGS = ['fr', 'en'];
function blogSlugs() { function blogSlugs() {
return fs return fs
.readdirSync(BLOG_ROOT) .readdirSync(BLOG_ROOT)
.filter((name) => fs.statSync(path.join(BLOG_ROOT, name)).isDirectory()); .filter((name) => fs.statSync(path.join(BLOG_ROOT, name)).isDirectory())
// A folder with neither meta.md nor a language file is not a post —
// notes, scratch, seeds-in-progress. It mints no slug and no route.
.filter((name) => {
const dir = path.join(BLOG_ROOT, name);
return (
fs.existsSync(path.join(dir, 'meta.md')) ||
BLOG_LANGS.some((lang) => fs.existsSync(path.join(dir, `${lang}.md`)))
);
});
} }
function langFile(slug, lang) { function langFile(slug, lang) {
......
...@@ -43,10 +43,18 @@ async function withProdEnv(fn) { ...@@ -43,10 +43,18 @@ async function withProdEnv(fn) {
} }
// Live roster from disk — the owner edits fixtures freely; the accessors // Live roster from disk — the owner edits fixtures freely; the accessors
// are what this suite pins, not any particular folder name. // are what this suite pins, not any particular folder name. Post-ness
// mirrors the loader: a folder with neither meta.md nor a language file
// (seed notes, scratch) is not a post.
const BLOG_ROOT = path.join(process.cwd(), "content", "blog");
const ALL_BLOG_SLUGS = fs const ALL_BLOG_SLUGS = fs
.readdirSync(path.join(process.cwd(), "content", "blog")) .readdirSync(BLOG_ROOT)
.filter((f) => !f.startsWith("_") && fs.statSync(path.join(process.cwd(), "content", "blog", f)).isDirectory()); .filter((f) => !f.startsWith("_") && fs.statSync(path.join(BLOG_ROOT, f)).isDirectory())
.filter(
(f) =>
fs.existsSync(path.join(BLOG_ROOT, f, "meta.md")) ||
BLOG_LANGS.some((lang) => fs.existsSync(path.join(BLOG_ROOT, f, `${lang}.md`)))
);
/** Whether a live folder is currently a draft (owner edits freely). */ /** Whether a live folder is currently a draft (owner edits freely). */
function readMetaDraft(slug) { function readMetaDraft(slug) {
...@@ -74,6 +82,11 @@ const DRAFT_DIR = path.join(process.cwd(), "content", "blog", DRAFT_SLUG); ...@@ -74,6 +82,11 @@ const DRAFT_DIR = path.join(process.cwd(), "content", "blog", DRAFT_SLUG);
const EN_SLUG = "_fixture-en-only"; const EN_SLUG = "_fixture-en-only";
const EN_DIR = path.join(process.cwd(), "content", "blog", EN_SLUG); const EN_DIR = path.join(process.cwd(), "content", "blog", EN_SLUG);
// Synthetic fr-only post — the fallback/banner tests need a single-language
// post they own; real posts gain translations as the owner writes them.
const FR_SLUG = "_fixture-fr-only";
const FR_DIR = path.join(process.cwd(), "content", "blog", FR_SLUG);
beforeAll(() => { beforeAll(() => {
fs.rmSync(FIXTURE_DIR, { recursive: true, force: true }); fs.rmSync(FIXTURE_DIR, { recursive: true, force: true });
fs.mkdirSync(FIXTURE_DIR, { recursive: true }); fs.mkdirSync(FIXTURE_DIR, { recursive: true });
...@@ -105,12 +118,19 @@ beforeAll(() => { ...@@ -105,12 +118,19 @@ beforeAll(() => {
path.join(EN_DIR, "en.md"), path.join(EN_DIR, "en.md"),
"---\ntitle: EN Fixture\n---\n\nEnglish only body.\n", "---\ntitle: EN Fixture\n---\n\nEnglish only body.\n",
); );
fs.rmSync(FR_DIR, { recursive: true, force: true });
fs.mkdirSync(FR_DIR, { recursive: true });
fs.writeFileSync(
path.join(FR_DIR, "fr.md"),
"---\ntitle: Sujet seul\n---\n\nCorps français seul.\n",
);
}); });
afterAll(() => { afterAll(() => {
fs.rmSync(FIXTURE_DIR, { recursive: true, force: true }); fs.rmSync(FIXTURE_DIR, { recursive: true, force: true });
fs.rmSync(DRAFT_DIR, { recursive: true, force: true }); fs.rmSync(DRAFT_DIR, { recursive: true, force: true });
fs.rmSync(EN_DIR, { recursive: true, force: true }); fs.rmSync(EN_DIR, { recursive: true, force: true });
fs.rmSync(FR_DIR, { recursive: true, force: true });
}); });
describe("blog public accessors (prod rule untouched)", () => { describe("blog public accessors (prod rule untouched)", () => {
...@@ -121,6 +141,17 @@ describe("blog public accessors (prod rule untouched)", () => { ...@@ -121,6 +141,17 @@ describe("blog public accessors (prod rule untouched)", () => {
); );
}); });
it("treats a folder with no meta.md and no language file as not-a-post", () => {
const scratch = path.join(BLOG_ROOT, "_notes-not-a-post");
fs.mkdirSync(scratch, { recursive: true });
fs.writeFileSync(path.join(scratch, "ideas.md"), "# seeds\n");
try {
expect(getAllBlogSlugs()).not.toContain("_notes-not-a-post");
} finally {
fs.rmSync(scratch, { recursive: true, force: true });
}
});
it("filters drafts out in prod — /blog/[slug] never renders them", async () => { it("filters drafts out in prod — /blog/[slug] never renders them", async () => {
await withProdEnv(() => { await withProdEnv(() => {
// The synthetic fixture is a published post, so it stays; every real // The synthetic fixture is a published post, so it stays; every real
...@@ -151,13 +182,11 @@ describe("blog public accessors (prod rule untouched)", () => { ...@@ -151,13 +182,11 @@ describe("blog public accessors (prod rule untouched)", () => {
const posts = getBlogPosts(); const posts = getBlogPosts();
const dates = posts.filter((p) => ALL_BLOG_SLUGS.includes(p.slug)).map((p) => p.date); const dates = posts.filter((p) => ALL_BLOG_SLUGS.includes(p.slug)).map((p) => p.date);
expect(dates).toEqual([...dates].sort().reverse()); expect(dates).toEqual([...dates].sort().reverse());
const again = posts.find((p) => p.slug === "hello-world-again"); const again = posts.find((p) => p.slug === FR_SLUG);
// The localized title (fr.md frontmatter) wins over meta.md's — compare // The localized title (fr.md frontmatter) wins over meta.md's — compare
// against the fixture's own fr.md so hand edits to the fixture don't // against the fixture's own fr.md so hand edits to the fixture don't
// falsify the accessor contract. // falsify the accessor contract.
const frTitle = matter( const frTitle = matter(fs.readFileSync(path.join(FR_DIR, "fr.md"), "utf8")).data.title;
fs.readFileSync(path.join("content/blog/hello-world-again/fr.md"), "utf8"),
).data.title;
expect(again.title).toBe(frTitle); expect(again.title).toBe(frTitle);
expect(again.languages).toEqual(["fr"]); expect(again.languages).toEqual(["fr"]);
}); });
...@@ -170,7 +199,7 @@ describe("blog public accessors (prod rule untouched)", () => { ...@@ -170,7 +199,7 @@ describe("blog public accessors (prod rule untouched)", () => {
describe("bilingual accessors", () => { describe("bilingual accessors", () => {
it("reports available languages from the folder contents", () => { it("reports available languages from the folder contents", () => {
expect(getBlogLanguages("hello-world-again")).toEqual(["fr"]); expect(getBlogLanguages(FR_SLUG)).toEqual(["fr"]);
expect(getBlogLanguages(EN_SLUG)).toEqual(["en"]); expect(getBlogLanguages(EN_SLUG)).toEqual(["en"]);
expect(getBlogLanguages(FIXTURE_SLUG)).toEqual(["fr", "en"]); expect(getBlogLanguages(FIXTURE_SLUG)).toEqual(["fr", "en"]);
}); });
...@@ -194,12 +223,12 @@ describe("bilingual accessors", () => { ...@@ -194,12 +223,12 @@ describe("bilingual accessors", () => {
it("falls back to the available language and flags the banner", async () => { it("falls back to the available language and flags the banner", async () => {
// fr requested on an fr-only post: no banner. // fr requested on an fr-only post: no banner.
const fr = await getBlogPost("hello-world-again"); const fr = await getBlogPost(FR_SLUG);
expect(fr.lang).toBe("fr"); expect(fr.lang).toBe("fr");
expect(fr.untranslated).toBeNull(); expect(fr.untranslated).toBeNull();
// en requested on an fr-only post: fr renders, banner data present. // en requested on an fr-only post: fr renders, banner data present.
const en = await getBlogPost("hello-world-again", "en"); const en = await getBlogPost(FR_SLUG, "en");
expect(en.lang).toBe("fr"); expect(en.lang).toBe("fr");
expect(en.untranslated).toBe("en"); expect(en.untranslated).toBe("en");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment