import assert from 'node:assert/strict'; import { rm, writeFile } from 'node:fs/promises'; import path from 'node:path'; import { after, before, test } from 'node:test'; import { createContentStore, loadContent, parseFrontmatter, youtubeId } from '../src/content.js'; import { formatDate, localize, pickLocale } from '../src/i18n.js'; import { createFixture } from './fixtures.js'; let dir; before(async () => { dir = await createFixture(); }); after(() => rm(dir, { recursive: true, force: true })); test('parseFrontmatter splits data and body', () => { const { data, body } = parseFrontmatter('---\ntitle: Hi\n---\nBody\n'); assert.equal(data.title, 'Hi'); assert.equal(body, 'Body\n'); assert.deepEqual(parseFrontmatter('no frontmatter').data, {}); }); test('youtubeId understands the usual URL shapes', () => { for (const url of [ 'https://youtu.be/dQw4w9WgXcQ', 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', 'https://www.youtube.com/watch?feature=share&v=dQw4w9WgXcQ', 'https://www.youtube.com/shorts/dQw4w9WgXcQ', 'https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ', ]) { assert.equal(youtubeId(url), 'dQw4w9WgXcQ', url); } assert.equal(youtubeId('https://vimeo.com/123'), null); }); test('i18n helpers', () => { assert.equal(localize({ de: 'Hallo', en: 'Hello' }, 'en'), 'Hello'); assert.equal(localize({ de: 'Nur Deutsch' }, 'en'), 'Nur Deutsch'); assert.equal(localize('plain', 'de'), 'plain'); assert.equal(pickLocale('en-US,en;q=0.9,de;q=0.8'), 'en'); assert.equal(pickLocale('fr-FR,de;q=0.5'), 'de'); assert.equal(pickLocale('fr'), 'de'); assert.equal(formatDate(2024, 'de'), '2024'); assert.equal(formatDate('2026-10', 'de'), 'Oktober 2026'); assert.equal(formatDate('2026-10-01', 'en'), 'October 2026'); }); test('drafts are hidden unless asked for', async () => { const { content } = await loadContent(dir); assert.deepEqual(content.projects.map((p) => p.slug), ['alpha']); assert.equal(content.homelab, null); assert.deepEqual(content.journey.map((e) => e.title), ['Live']); const { content: withDrafts } = await loadContent(dir, { showDrafts: true }); assert.deepEqual(withDrafts.projects.map((p) => p.slug), ['alpha', 'beta']); assert.ok(withDrafts.homelab); }); test('projects: fallback language, images, video', async () => { const { content, warnings } = await loadContent(dir, { showDrafts: true }); const alpha = content.projects.find((p) => p.slug === 'alpha'); assert.equal(alpha.text.de.title, 'Alpha'); assert.equal(alpha.text.en.title, 'Alpha'); assert.equal(alpha.text.en.fallback, 'de'); assert.equal(alpha.text.de.fallback, undefined); assert.equal(alpha.cover.width, 1200); assert.match(alpha.cover.srcset, /w=480&v=\w+ 480w, .*w=960.* 960w, .*w=1600.* 1200w$/); assert.match(alpha.text.de.html, / p.slug === 'beta'); assert.equal(beta.text.de.fallback, 'en'); assert.ok(warnings.some((w) => w.startsWith('projects/broken/meta.yml:')), 'broken project is reported'); assert.ok(!content.projects.some((p) => p.slug === 'broken')); }); test('a broken site.yml keeps the previous content', async () => { const store = createContentStore({ contentDir: dir, showDrafts: false }); await store.reload(); const before = store.get(); const file = path.join(dir, 'site.yml'); await writeFile(file, 'name: [unclosed\n'); await assert.rejects(store.reload()); assert.equal(store.get(), before); await writeFile(file, 'name: Test\n'); });