[READ-ONLY] a fast, modern browser for the npm registry
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

test: add tests for parse-basic-frontmatter (#961)

authored by

James Garbutt and committed by
GitHub
e54df82b c8fcc6ee

+130 -25
+22 -25
shared/utils/parse-basic-frontmatter.ts
··· 2 2 const match = fileContent.match(/^---\s*\n([\s\S]*?)\n---\s*(?:\n|$)/) 3 3 if (!match?.[1]) return {} 4 4 5 - return match[1].split('\n').reduce( 6 - (acc, line) => { 7 - const idx = line.indexOf(':') 8 - if (idx === -1) return acc 5 + return match[1].split('\n').reduce<Record<string, unknown>>((acc, line) => { 6 + const idx = line.indexOf(':') 7 + if (idx === -1) return acc 9 8 10 - const key = line.slice(0, idx).trim() 9 + const key = line.slice(0, idx).trim() 11 10 12 - // Remove surrounding quotes 13 - let value = line 14 - .slice(idx + 1) 15 - .trim() 16 - .replace(/^["']|["']$/g, '') 11 + // Remove surrounding quotes 12 + let value = line 13 + .slice(idx + 1) 14 + .trim() 15 + .replace(/^["']|["']$/g, '') 17 16 18 - // Type coercion (handles 123, 45.6, boolean, arrays) 19 - if (value === 'true') acc[key] = true 20 - else if (value === 'false') acc[key] = false 21 - else if (/^-?\d+$/.test(value)) acc[key] = parseInt(value, 10) 22 - else if (/^-?\d+\.\d+$/.test(value)) acc[key] = parseFloat(value) 23 - else if (value.startsWith('[') && value.endsWith(']')) { 24 - acc[key] = value 25 - .slice(1, -1) 26 - .split(',') 27 - .map(s => s.trim().replace(/^["']|["']$/g, '')) 28 - } else acc[key] = value 17 + // Type coercion (handles 123, 45.6, boolean, arrays) 18 + if (value === 'true') acc[key] = true 19 + else if (value === 'false') acc[key] = false 20 + else if (/^-?\d+$/.test(value)) acc[key] = parseInt(value, 10) 21 + else if (/^-?\d+\.\d+$/.test(value)) acc[key] = parseFloat(value) 22 + else if (value.startsWith('[') && value.endsWith(']')) { 23 + acc[key] = value 24 + .slice(1, -1) 25 + .split(',') 26 + .map(s => s.trim().replace(/^["']|["']$/g, '')) 27 + } else acc[key] = value 29 28 30 - return acc 31 - }, 32 - {} as Record<string, unknown>, 33 - ) 29 + return acc 30 + }, {}) 34 31 }
+108
test/unit/shared/utils/parse-basic-frontmatter.spec.ts
··· 1 + import { describe, expect, it } from 'vitest' 2 + import { parseBasicFrontmatter } from '../../../../shared/utils/parse-basic-frontmatter' 3 + 4 + describe('parseBasicFrontmatter', () => { 5 + it('returns empty object for content without frontmatter', () => { 6 + expect(parseBasicFrontmatter('just some text')).toEqual({}) 7 + }) 8 + 9 + it('returns empty object for empty string', () => { 10 + expect(parseBasicFrontmatter('')).toEqual({}) 11 + }) 12 + 13 + it('returns empty object for empty frontmatter block', () => { 14 + expect(parseBasicFrontmatter('---\n---\n')).toEqual({}) 15 + }) 16 + 17 + it('parses string values', () => { 18 + const input = '---\ntitle: Hello World\nauthor: James\n---\n' 19 + expect(parseBasicFrontmatter(input)).toEqual({ 20 + title: 'Hello World', 21 + author: 'James', 22 + }) 23 + }) 24 + 25 + it('strips surrounding quotes from values', () => { 26 + const input = '---\ntitle: "Hello World"\nauthor: \'James\'\n---\n' 27 + expect(parseBasicFrontmatter(input)).toEqual({ 28 + title: 'Hello World', 29 + author: 'James', 30 + }) 31 + }) 32 + 33 + it('parses boolean true', () => { 34 + const input = '---\ndraft: true\n---\n' 35 + expect(parseBasicFrontmatter(input)).toEqual({ draft: true }) 36 + }) 37 + 38 + it('parses boolean false', () => { 39 + const input = '---\ndraft: false\n---\n' 40 + expect(parseBasicFrontmatter(input)).toEqual({ draft: false }) 41 + }) 42 + 43 + it('parses integer values', () => { 44 + const input = '---\ncount: 42\nnegative: -7\n---\n' 45 + expect(parseBasicFrontmatter(input)).toEqual({ count: 42, negative: -7 }) 46 + }) 47 + 48 + it('parses float values', () => { 49 + const input = '---\nrating: 4.5\nnegative: -3.14\n---\n' 50 + expect(parseBasicFrontmatter(input)).toEqual({ rating: 4.5, negative: -3.14 }) 51 + }) 52 + 53 + it('parses array values', () => { 54 + const input = '---\ntags: [foo, bar, baz]\n---\n' 55 + expect(parseBasicFrontmatter(input)).toEqual({ 56 + tags: ['foo', 'bar', 'baz'], 57 + }) 58 + }) 59 + 60 + it('strips quotes from array items', () => { 61 + const input = '---\ntags: ["foo", \'bar\']\n---\n' 62 + expect(parseBasicFrontmatter(input)).toEqual({ 63 + tags: ['foo', 'bar'], 64 + }) 65 + }) 66 + 67 + it('does not support nested arrays', () => { 68 + const input = '---\nmatrix: [[1, 2], [3, 4]]\n---\n' 69 + const result = parseBasicFrontmatter(input) 70 + expect(result.matrix).toEqual(['[1', '2]', '[3', '4]']) 71 + }) 72 + 73 + it('handles values with colons', () => { 74 + const input = '---\nurl: https://example.com\n---\n' 75 + expect(parseBasicFrontmatter(input)).toEqual({ 76 + url: 'https://example.com', 77 + }) 78 + }) 79 + 80 + it('skips lines without colons', () => { 81 + const input = '---\ntitle: Hello\ninvalid line\nauthor: James\n---\n' 82 + expect(parseBasicFrontmatter(input)).toEqual({ 83 + title: 'Hello', 84 + author: 'James', 85 + }) 86 + }) 87 + 88 + it('trims keys and values', () => { 89 + const input = '---\n title : Hello \n---\n' 90 + expect(parseBasicFrontmatter(input)).toEqual({ title: 'Hello' }) 91 + }) 92 + 93 + it('handles frontmatter at end of file without trailing newline', () => { 94 + const input = '---\ntitle: Hello\n---' 95 + expect(parseBasicFrontmatter(input)).toEqual({ title: 'Hello' }) 96 + }) 97 + 98 + it('handles mixed types', () => { 99 + const input = '---\ntitle: My Post\ncount: 5\nrating: 9.8\npublished: true\ntags: [a, b]\n---\n' 100 + expect(parseBasicFrontmatter(input)).toEqual({ 101 + title: 'My Post', 102 + count: 5, 103 + rating: 9.8, 104 + published: true, 105 + tags: ['a', 'b'], 106 + }) 107 + }) 108 + })