| import { describe, expect, test } from 'vitest' | |
| import parse from '@/frame/lib/read-frontmatter' | |
| import { schema as frontmatterSchema } from '@/frame/lib/frontmatter' | |
| const filepath = 'path/to/file.md' | |
| const fixture1 = `--- | |
| title: Hello, World | |
| meaning_of_life: 42 | |
| --- | |
| I am content. | |
| ` | |
| const fixture2 = `--- | |
| versions: | |
| fpt: '*' | |
| ghec: '*' | |
| ghes: 'BAD_VERSION' | |
| --- | |
| ` | |
| describe('frontmatter', () => { | |
| test('parses frontmatter and content in a given string (no options required)', () => { | |
| const { data, content, errors } = parse(fixture1) | |
| expect(data!.title).toBe('Hello, World') | |
| expect(data!.meaning_of_life).toBe(42) | |
| expect(content!.trim()).toBe('I am content.') | |
| expect(errors.length).toBe(0) | |
| }) | |
| describe('frontmatter.stringify', () => { | |
| test('is exported', () => { | |
| expect(typeof parse.stringify).toBe('function') | |
| }) | |
| }) | |
| describe('YML parsing errors', () => { | |
| test('creates errors if YML has an unescaped quote', () => { | |
| const fixture = `--- | |
| intro: 'I've got an unescaped quote' | |
| --- | |
| I am content. | |
| ` | |
| const { errors } = parse(fixture, { filepath }) | |
| expect(errors.length).toBe(1) | |
| const expectedError = { | |
| filepath: 'path/to/file.md', | |
| message: 'YML parsing error!', | |
| reason: 'invalid frontmatter entry', | |
| } | |
| expect(errors[0]).toEqual(expectedError) | |
| }) | |
| test('creates errors if YML has incorrect indentation', () => { | |
| const fixture = `--- | |
| title: Hello, World | |
| intro: 'I have a bad leading space' | |
| --- | |
| I am content. | |
| ` | |
| const { errors } = parse(fixture, { filepath }) | |
| expect(errors.length).toBe(1) | |
| const expectedError = { | |
| filepath: 'path/to/file.md', | |
| message: 'YML parsing error!', | |
| reason: 'invalid frontmatter entry', | |
| } | |
| expect(errors[0]).toEqual(expectedError) | |
| }) | |
| }) | |
| describe('schema', () => { | |
| test('is optional', () => { | |
| const schema = { | |
| properties: { | |
| title: { | |
| type: 'string', | |
| }, | |
| meaning_of_life: { | |
| type: 'number', | |
| }, | |
| }, | |
| } | |
| const { data, content, errors } = parse(fixture1, { schema }) | |
| expect(data!.title).toBe('Hello, World') | |
| expect(data!.meaning_of_life).toBe(42) | |
| expect(content!.trim()).toBe('I am content.') | |
| expect(errors.length).toBe(0) | |
| }) | |
| test('creates errors if frontmatter does not conform to schema', () => { | |
| const schema = { | |
| properties: { | |
| meaning_of_life: { | |
| type: 'number', | |
| minimum: 50, | |
| }, | |
| }, | |
| } | |
| const { data, content, errors } = parse(fixture1, { schema }) | |
| expect(data!.title).toBe('Hello, World') | |
| expect(data!.meaning_of_life).toBe(42) | |
| expect(content!.trim()).toBe('I am content.') | |
| expect(errors.length).toBe(1) | |
| const expectedError = { | |
| instancePath: '/meaning_of_life', | |
| schemaPath: '#/properties/meaning_of_life/minimum', | |
| keyword: 'minimum', | |
| params: { | |
| comparison: '>=', | |
| limit: 50, | |
| }, | |
| message: 'must be >= 50', | |
| } | |
| expect(errors[0]).toEqual(expectedError) | |
| }) | |
| test('creates errors if versions frontmatter does not match semver format', () => { | |
| const schema = { type: 'object', required: ['versions'], properties: {} } | |
| ;(schema.properties as any).versions = Object.assign( | |
| {}, | |
| (frontmatterSchema.properties as any).versions, | |
| ) | |
| const { errors } = parse(fixture2, { schema }) | |
| const expectedError = { | |
| instancePath: '/versions/ghes', | |
| schemaPath: '#/properties/versions/properties/ghes/errorMessage', | |
| keyword: 'errorMessage', | |
| params: { | |
| errors: [ | |
| { | |
| instancePath: '/versions/ghes', | |
| schemaPath: '#/properties/versions/properties/ghes/format', | |
| keyword: 'format', | |
| params: { | |
| format: 'semver', | |
| }, | |
| message: 'must match format "semver"', | |
| emUsed: true, | |
| }, | |
| ], | |
| }, | |
| message: 'Must be a valid SemVer range: "BAD_VERSION"', | |
| } | |
| expect(errors[0]).toEqual(expectedError) | |
| }) | |
| test('creates errors if required frontmatter is not present', () => { | |
| const schema = { | |
| type: 'object', | |
| required: ['yet_another_key'], | |
| properties: { | |
| yet_another_key: { | |
| type: 'string', | |
| }, | |
| }, | |
| } | |
| const { errors } = parse(fixture1, { schema }) | |
| expect(errors.length).toBe(1) | |
| const expectedError = { | |
| instancePath: '', | |
| schemaPath: '#/required', | |
| keyword: 'required', | |
| params: { | |
| missingProperty: 'yet_another_key', | |
| }, | |
| message: "must have required property 'yet_another_key'", | |
| } | |
| expect(errors[0]).toEqual(expectedError) | |
| }) | |
| }) | |
| }) | |