UNPKG

@simplepg/repo

Version:

SimplePage repository

1,212 lines (1,028 loc) 29.1 kB
import { JSDOM } from 'jsdom' import { parseFrontmatter, populateTemplate } from '../src/utils/template.js' const dom = new JSDOM() global.DOMParser = dom.window.DOMParser describe('parseFrontmatter', () => { describe('basic functionality', () => { test('should parse title and description', () => { const markdown = `--- title: My Page description: This is a test page --- # Content goes here` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page', description: 'This is a test page' }) }) test('should handle quoted values', () => { const markdown = `--- title: "My Page" description: 'This is a test page' --- # Content goes here` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page', description: 'This is a test page' }) }) test('should return empty object for markdown without frontmatter', () => { const markdown = `# Content without frontmatter` const result = parseFrontmatter(markdown) expect(result).toEqual({}) }) test('should return empty object for empty markdown', () => { const result = parseFrontmatter('') expect(result).toEqual({}) }) test('should handle empty frontmatter', () => { const markdown = `--- --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({}) }) }) describe('boolean fields', () => { test('should parse sidebar-toc boolean', () => { const markdown = `--- sidebar-toc: true --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ 'sidebar-toc': true }) }) test('should parse rss boolean', () => { const markdown = `--- rss: true --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ rss: true }) }) test('should handle false boolean values', () => { const markdown = `--- sidebar-toc: false rss: false --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ 'sidebar-toc': false, rss: false }) }) test('should be case insensitive for boolean values', () => { const markdown = `--- sidebar-toc: True rss: FALSE --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ 'sidebar-toc': true, rss: false }) }) }) describe('number fields', () => { test('should parse sidebar-nav-prio as integer', () => { const markdown = `--- sidebar-nav-prio: 5 --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ 'sidebar-nav-prio': 5 }) }) test('should handle zero priority', () => { const markdown = `--- sidebar-nav-prio: 0 --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ 'sidebar-nav-prio': 0 }) }) test('should handle negative priority', () => { const markdown = `--- sidebar-nav-prio: -5 --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ 'sidebar-nav-prio': -5 }) }) test('should parse integer from string', () => { const markdown = `--- sidebar-nav-prio: 10 --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ 'sidebar-nav-prio': 10 }) }) }) describe('date fields', () => { test('should parse created date', () => { const markdown = `--- created: 2024-01-15 --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ created: '2024-01-15' }) }) test('should parse updated date', () => { const markdown = `--- updated: 2024-01-16T12:00:00Z --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ updated: '2024-01-16T12:00:00Z' }) }) test('should parse both created and updated', () => { const markdown = `--- created: 2024-01-15 updated: 2024-01-16T12:00:00Z --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ created: '2024-01-15', updated: '2024-01-16T12:00:00Z' }) }) test('should handle quoted dates', () => { const markdown = `--- created: "2024-01-15" updated: '2024-01-16' --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ created: '2024-01-15', updated: '2024-01-16' }) }) test('should handle date only format (YYYY-MM-DD)', () => { const markdown = `--- created: 2025-02-12 --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ created: '2025-02-12' }) }) test('should handle full ISO timestamp', () => { const markdown = `--- created: 2024-01-15T10:00:00Z --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ created: '2024-01-15T10:00:00Z' }) }) }) describe('tags parsing', () => { test('should parse comma-separated tags', () => { const markdown = `--- tags: tutorial, introduction, getting-started --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial', 'introduction', 'getting-started'] }) }) test('should parse YAML list format tags', () => { const markdown = `--- tags: - tutorial - introduction --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial', 'introduction'] }) }) test('should handle quoted tags in list', () => { const markdown = `--- tags: - "tutorial" - 'introduction' --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial', 'introduction'] }) }) test('should parse array notation tags', () => { const markdown = `--- tags: [tutorial, introduction, getting-started] --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial', 'introduction', 'getting-started'] }) }) test('should parse single tag', () => { const markdown = `--- tags: tutorial --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial'] }) }) test('should handle empty tags list', () => { const markdown = `--- tags: [] --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: [] }) }) test('should handle tags list with empty value', () => { const markdown = `--- tags: --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: [] }) }) test('should handle spaces in tags', () => { const markdown = `--- tags: getting started, web development --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['getting started', 'web development'] }) }) test('should filter out empty tags', () => { const markdown = `--- tags: tutorial, , introduction, --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial', 'introduction'] }) }) }) describe('language field', () => { test('should parse language field', () => { const markdown = `--- language: en --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ language: 'en' }) }) test('should handle language codes with region', () => { const markdown = `--- language: en-US --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ language: 'en-US' }) }) }) describe('edge cases', () => { test('should handle extra whitespace', () => { const markdown = `--- title: My Page description: This is a test page --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page', description: 'This is a test page' }) }) test('should handle empty lines in frontmatter', () => { const markdown = `--- title: My Page description: This is a test page --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page', description: 'This is a test page' }) }) test('should handle colons in values', () => { const markdown = `--- title: My Page: A Guide description: This is: a test page --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page: A Guide', description: 'This is: a test page' }) }) test('should handle special characters in values', () => { const markdown = `--- title: My Page & More description: This is a test (page) with @special #chars --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page & More', description: 'This is a test (page) with @special #chars' }) }) test('should handle unicode characters', () => { const markdown = `--- title: 我的页面 description: 这是一个测试页面 --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: '我的页面', description: '这是一个测试页面' }) }) test('should handle newlines in quoted values (first line only)', () => { const markdown = `--- title: "My Page with newlines" description: Regular description --- # Content` const result = parseFrontmatter(markdown) // The parser only handles the first line before multiline values expect(result).toEqual({ title: '"My Page', // Only parses first line when newline is in value description: 'Regular description' }) }) test('should ignore unknown fields', () => { const markdown = `--- title: My Page unknown-field: some value another-field: another value --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page' }) }) test('should handle very long field values', () => { const longDescription = 'A'.repeat(1000) const markdown = `--- title: My Page description: ${longDescription} --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page', description: longDescription }) }) test('should handle mixed case field names', () => { const markdown = `--- Title: My Page Description: This is a test page rSS: true --- # Content` const result = parseFrontmatter(markdown) // Field names must be lowercase (title, description, rss) to be parsed expect(result).toEqual({}) }) test('should handle frontmatter with extra dashes', () => { const markdown = `--- title: My Page description: This is a test page --- Some text with --- dashes here # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page', description: 'This is a test page' }) }) test('should handle missing closing frontmatter delimiter', () => { const markdown = `--- title: My Page description: This is a test page # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({}) }) test('should handle content starting with dashes', () => { const markdown = `--- title: My Page --- # Content with --- dashes Some more content here` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page' }) }) }) describe('complex combinations', () => { test('should parse all field types together', () => { const markdown = `--- title: My Blog Post description: A comprehensive guide to RSS feeds created: 2024-01-15 updated: 2024-01-16T12:00:00Z tags: - tutorial - rss - web-development sidebar-toc: true rss: true sidebar-nav-prio: 5 language: en --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Blog Post', description: 'A comprehensive guide to RSS feeds', created: '2024-01-15', updated: '2024-01-16T12:00:00Z', tags: ['tutorial', 'rss', 'web-development'], 'sidebar-toc': true, rss: true, 'sidebar-nav-prio': 5, language: 'en' }) }) test('should handle multiple date formats', () => { const markdown = `--- created: 2025-02-12 updated: "2025-02-13T14:30:00Z" --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ created: '2025-02-12', updated: '2025-02-13T14:30:00Z' }) }) test('should handle both tag formats in different frontmatters', () => { // Test comma-separated const markdown1 = `--- tags: tutorial, introduction --- # Content` const result1 = parseFrontmatter(markdown1) expect(result1).toEqual({ tags: ['tutorial', 'introduction'] }) // Test YAML list const markdown2 = `--- tags: - tutorial - introduction --- # Content` const result2 = parseFrontmatter(markdown2) expect(result2).toEqual({ tags: ['tutorial', 'introduction'] }) // Results should be identical expect(result1).toEqual(result2) }) }) describe('malformed frontmatter', () => { test('should handle lines without colons gracefully', () => { const markdown = `--- title: My Page this line has no colon description: This is a test page --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page', description: 'This is a test page' }) }) test('should handle empty values', () => { const markdown = `--- title: My Page description: tags: --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page', description: '', // Empty values are preserved tags: [] }) }) test('should handle duplicate keys (take last one)', () => { const markdown = `--- title: First Title title: Second Title --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'Second Title' }) }) test('should handle tags without proper formatting', () => { const markdown = `--- tags: [tutorial , introduction ] --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial', 'introduction'] }) }) test('should handle tags with varying quote styles', () => { const markdown = `--- tags: ["tutorial", 'introduction', getting-started] --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial', 'introduction', 'getting-started'] }) }) test('should handle mixed tag formats', () => { const markdown = `--- tags: [tutorial, "introduction", getting-started, 'advanced'] --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial', 'introduction', 'getting-started', 'advanced'] }) }) test('should handle single tag in brackets', () => { const markdown = `--- tags: [tutorial] --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial'] }) }) test('should handle quoted single tag', () => { const markdown = `--- tags: "tutorial" --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial'] }) }) test('should handle tags with commas in values', () => { const markdown = `--- tags: [getting started, "web dev, frontend", advanced] --- # Content` const result = parseFrontmatter(markdown) // The parser splits on commas and strips quotes/whitespace from individual items expect(result).toEqual({ tags: ['getting started', 'web dev', 'frontend', 'advanced'] }) }) test('should handle nested brackets (only outer)', () => { const markdown = `--- tags: [[nested], tutorial] --- # Content` const result = parseFrontmatter(markdown) // Outer brackets are stripped, inner stays as content, spaces trimmed expect(result).toEqual({ tags: ['[nested]', 'tutorial'] }) }) test('should handle tags with special YAML characters', () => { const markdown = `--- tags: [c++, node.js, "c#"] --- # Content` const result = parseFrontmatter(markdown) // Brackets stripped, then quotes stripped from individual items expect(result).toEqual({ tags: ['c++', 'node.js', 'c#'] }) }) test('should handle tags list with items on same line', () => { const markdown = `--- tags: - tutorial - introduction --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial - introduction'] }) }) test('should handle tags list with trailing commas in items', () => { const markdown = `--- tags: - tutorial, - introduction --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial,', 'introduction'] }) }) test('should handle empty tags with brackets', () => { const markdown = `--- tags: [] --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: [] }) }) test('should handle tag with empty string', () => { const markdown = `--- tags: "" --- # Content` const result = parseFrontmatter(markdown) // Empty strings are filtered out by .filter(Boolean) expect(result).toEqual({ tags: [] }) }) test('should handle tag value with only commas', () => { const markdown = `--- tags: [,,] --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: [] }) }) test('should handle tags with different indentation in list', () => { const markdown = `--- tags: - tutorial - introduction --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial', 'introduction'] }) }) test('should handle tags list with comments-like text', () => { const markdown = `--- tags: - tutorial - introduction # comment here should be part of tag --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['tutorial', 'introduction # comment here should be part of tag'] }) }) test('should handle malformed tags with mixed dashes and commas', () => { const markdown = `--- tags: tutorial, -introduction --- # Content` const result = parseFrontmatter(markdown) // Spaces trimmed from individual items expect(result).toEqual({ tags: ['tutorial', '-introduction'] }) }) test('should handle tags with unclosed brackets', () => { const markdown = `--- tags: [tutorial, introduction --- # Content` const result = parseFrontmatter(markdown) // Leading [ is captured in first tag when no closing ] expect(result).toEqual({ tags: ['[tutorial', 'introduction'] }) }) test('should handle tags with extra opening bracket', () => { const markdown = `--- tags: [[tutorial, introduction] --- # Content` const result = parseFrontmatter(markdown) // Only first [ is stripped, second [ stays, last ] is stripped expect(result).toEqual({ tags: ['[tutorial', 'introduction'] }) }) test('should handle multiple tag fields (takes last)', () => { const markdown = `--- tags: first tags: second tags: third --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ tags: ['third'] }) }) test('should handle tags that look like other fields', () => { const markdown = `--- title: My Page tags: - title: not a title - description: not description --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page', tags: ['title: not a title', 'description: not description'] }) }) }) describe('badly formatted YAML - general issues', () => { test('should handle missing spaces after colon', () => { const markdown = `--- title:My Page description:This is a test --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page', description: 'This is a test' }) }) test('should handle extra spaces before colon', () => { const markdown = `--- title : My Page description : This is a test --- # Content` const result = parseFrontmatter(markdown) // Key is trimmed before checking whitelist expect(result).toEqual({ title: 'My Page', description: 'This is a test' }) }) test('should handle tabs instead of spaces', () => { const markdown = `--- title: My Page description: This is a test --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page', description: 'This is a test' }) }) test('should handle values with newlines (first line only)', () => { const markdown = `--- title: My Page description: Second part --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My', description: 'Second part' }) }) test('should handle frontmatter with only one dash', () => { const markdown = `- title: My Page - # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({}) }) test('should handle frontmatter with three dashes after', () => { const markdown = `--- title: My Page --- --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page' }) }) test('should handle quoted keys', () => { const markdown = `--- "title": "My Page" 'description': 'This is a test' --- # Content` const result = parseFrontmatter(markdown) // Keys with quotes are not whitelisted, so they're ignored expect(result).toEqual({}) }) test('should handle keys with colons in them', () => { const markdown = `--- title: My Page description:value: This is a test --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page', description: 'value: This is a test' }) }) test('should handle values starting with hash', () => { const markdown = `--- title: # My Page description: # This is a test --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: '# My Page', description: '# This is a test' }) }) test('should handle values that look like YAML', () => { const markdown = `--- title: My Page description: something: value tags: [item1, item2: value] --- # Content` const result = parseFrontmatter(markdown) // Regex strips brackets, including content after : expect(result).toEqual({ title: 'My Page', description: 'something: value', tags: ['item1', 'item2: value'] }) }) test('should handle boolean values without key', () => { const markdown = `--- title: My Page true false --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page' }) }) test('should handle numeric-looking strings', () => { const markdown = `--- title: 42 description: 0 sidebar-nav-prio: "100" --- # Content` const result = parseFrontmatter(markdown) // Quoted numbers lose quotes but are still strings expect(result).toEqual({ title: '42', description: '0', 'sidebar-nav-prio': NaN // Quoted strings with numbers become NaN when parseInt }) }) test('should handle pipe character in values', () => { const markdown = `--- title: My Page | Section description: A | B | C --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My Page | Section', description: 'A | B | C' }) }) test('should handle asterisks and other special chars', () => { const markdown = `--- title: My * Page description: This (is) a test {value} [brackets] --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ title: 'My * Page', description: 'This (is) a test {value} [brackets]' }) }) test('should handle empty key', () => { const markdown = `--- : empty key title: My Page --- # Content` const result = parseFrontmatter(markdown) // Empty key doesn't match the regex pattern expect(result).toEqual({ title: 'My Page' }) }) test('should handle very long keys', () => { const longKey = 'title'.repeat(100) const markdown = `--- ${longKey}: My Page --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({}) }) test('should handle malformed closing delimiter', () => { const markdown = `--- title: My Page -- - --- # Content` const result = parseFrontmatter(markdown) // Malformed delimiter still picks up the content between dashes expect(result).toEqual({ title: 'My Page' }) }) test('should handle incomplete boolean values', () => { const markdown = `--- sidebar-toc: tru rss: fals --- # Content` const result = parseFrontmatter(markdown) expect(result).toEqual({ 'sidebar-toc': false, rss: false }) }) }) }) describe('populateTemplate', () => { const templateHtml = `<!DOCTYPE html> <html lang="en"> <head> <title>Template</title> <link rel="icon" href="/_assets/images/favicon-light.png" media="(prefers-color-scheme: light)"> <link rel="icon" href="/_assets/images/favicon-dark.png" media="(prefers-color-scheme: dark)"> </head> <body> <div id="content-container"></div> </body> </html>` const bodyHtml = '<p>Body</p>' test('should add apple-touch-icon for raster avatars', () => { const html = populateTemplate(templateHtml, bodyHtml, 'test.eth', '/', {}, '/_files/.artifacts/ensAvatar.png') const doc = new DOMParser().parseFromString(html, 'text/html') const appleTouchIcon = doc.querySelector('link[rel="apple-touch-icon"]') expect(appleTouchIcon).not.toBeNull() expect(appleTouchIcon.getAttribute('href')).toBe('/_files/.artifacts/ensAvatar.png') }) test('should fall back to default apple-touch-icon for svg avatars', () => { const html = populateTemplate(templateHtml, bodyHtml, 'test.eth', '/', {}, '/_files/.artifacts/ensAvatar.svg') const doc = new DOMParser().parseFromString(html, 'text/html') const appleTouchIcon = doc.querySelector('link[rel="apple-touch-icon"]') expect(appleTouchIcon).not.toBeNull() expect(appleTouchIcon.getAttribute('href')).toBe('/_assets/images/logo.png') }) test('should prefer provided favicons but keep avatar for social meta', () => { const faviconPaths = { light: '/_files/.artifacts/foamFavicon-light.png', dark: '/_files/.artifacts/foamFavicon-dark.png' } const avatarPath = '/_files/.artifacts/ensAvatar.png' const html = populateTemplate(templateHtml, bodyHtml, 'test.eth', '/', {}, avatarPath, '.link', faviconPaths) const doc = new DOMParser().parseFromString(html, 'text/html') const icons = Array.from(doc.querySelectorAll('link[rel="icon"]')) const iconHrefs = icons.map(icon => icon.getAttribute('href')) expect(iconHrefs).toContain('/_files/.artifacts/foamFavicon-light.png') expect(iconHrefs).toContain('/_files/.artifacts/foamFavicon-dark.png') const ogImage = doc.querySelector('meta[property="og:image"]') expect(ogImage).toBeDefined() expect(ogImage.content).toBe('https://test.eth.link/_files/.artifacts/ensAvatar.png') const twitterImage = doc.querySelector('meta[name="twitter:image"]') expect(twitterImage).toBeDefined() expect(twitterImage.content).toBe('https://test.eth.link/_files/.artifacts/ensAvatar.png') }) })