- lib/product-doc-parser.ts: parseProductDocMd(md) → {ok, frontmatter, body}
| {ok:false, errors[]} met line-info bij YAML-fouten. Pattern gespiegeld
uit lib/idea-plan-parser.ts.
- lib/product-doc-frontmatter.ts: setProductDocFrontmatterFields(md, patch)
laat de server `last_updated` server-side normaliseren (P2-review-fix
uit docs/recommendations/product-docs-storage-system-review-2026-05-16).
Gebruikt yaml.parseDocument om field-ordering best-effort te behouden.
- todayIsoDate() helper voor 'yyyy-mm-dd' string.
- __tests__: 19 nieuwe tests groen — parse-success/fail-paden, en
expliciete P2-coverage (vervangen + toevoegen last_updated, behoud
overige velden + body).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
141 lines
2.9 KiB
TypeScript
141 lines
2.9 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
|
|
import { parseProductDocMd } from '@/lib/product-doc-parser'
|
|
|
|
const minimalValid = `---
|
|
title: "Deploy stappen"
|
|
status: draft
|
|
---
|
|
|
|
# Body
|
|
|
|
stappen hier
|
|
`
|
|
|
|
describe('parseProductDocMd — succes', () => {
|
|
it('parseert minimaal valide doc', () => {
|
|
const r = parseProductDocMd(minimalValid)
|
|
expect(r.ok).toBe(true)
|
|
if (!r.ok) return
|
|
expect(r.frontmatter.title).toBe('Deploy stappen')
|
|
expect(r.frontmatter.status).toBe('draft')
|
|
expect(r.body.startsWith('# Body')).toBe(true)
|
|
})
|
|
|
|
it('accepteert optionele velden (audience, applies_to, last_updated)', () => {
|
|
const md = `---
|
|
title: "Doc"
|
|
status: active
|
|
audience: [maintainer, contributor]
|
|
applies_to: PBI-96
|
|
last_updated: 2026-05-16
|
|
---
|
|
|
|
body
|
|
`
|
|
const r = parseProductDocMd(md)
|
|
expect(r.ok).toBe(true)
|
|
if (!r.ok) return
|
|
expect(r.frontmatter.audience).toEqual(['maintainer', 'contributor'])
|
|
expect(r.frontmatter.applies_to).toBe('PBI-96')
|
|
expect(r.frontmatter.last_updated).toBe('2026-05-16')
|
|
})
|
|
|
|
it('accepteert audience als single string', () => {
|
|
const md = `---
|
|
title: "Doc"
|
|
status: draft
|
|
audience: maintainer
|
|
---
|
|
|
|
body`
|
|
const r = parseProductDocMd(md)
|
|
expect(r.ok).toBe(true)
|
|
if (!r.ok) return
|
|
expect(r.frontmatter.audience).toBe('maintainer')
|
|
})
|
|
|
|
it('trimt leading whitespace van body', () => {
|
|
const md = `---
|
|
title: "x"
|
|
status: draft
|
|
---
|
|
|
|
|
|
body
|
|
`
|
|
const r = parseProductDocMd(md)
|
|
expect(r.ok).toBe(true)
|
|
if (!r.ok) return
|
|
expect(r.body.startsWith('body')).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('parseProductDocMd — fouten', () => {
|
|
it('weigert doc zonder frontmatter (regel 1 error)', () => {
|
|
const r = parseProductDocMd('# alleen body')
|
|
expect(r.ok).toBe(false)
|
|
if (r.ok) return
|
|
expect(r.errors[0].line).toBe(1)
|
|
expect(r.errors[0].message).toMatch(/yaml-frontmatter/i)
|
|
})
|
|
|
|
it('weigert doc zonder afsluitende `---`', () => {
|
|
const md = `---
|
|
title: "x"
|
|
status: draft
|
|
|
|
body
|
|
`
|
|
const r = parseProductDocMd(md)
|
|
expect(r.ok).toBe(false)
|
|
})
|
|
|
|
it('weigert frontmatter zonder title', () => {
|
|
const md = `---
|
|
status: draft
|
|
---
|
|
|
|
body`
|
|
const r = parseProductDocMd(md)
|
|
expect(r.ok).toBe(false)
|
|
if (r.ok) return
|
|
expect(r.errors.some((e) => e.message.includes('title'))).toBe(true)
|
|
})
|
|
|
|
it('weigert frontmatter zonder status', () => {
|
|
const md = `---
|
|
title: "x"
|
|
---
|
|
|
|
body`
|
|
const r = parseProductDocMd(md)
|
|
expect(r.ok).toBe(false)
|
|
if (r.ok) return
|
|
expect(r.errors.some((e) => e.message.includes('status'))).toBe(true)
|
|
})
|
|
|
|
it('weigert status buiten enum-set', () => {
|
|
const md = `---
|
|
title: "x"
|
|
status: wip
|
|
---
|
|
|
|
body`
|
|
const r = parseProductDocMd(md)
|
|
expect(r.ok).toBe(false)
|
|
})
|
|
|
|
it('geeft line-info bij bad yaml', () => {
|
|
const md = `---
|
|
title: "x
|
|
status: draft
|
|
---
|
|
|
|
body`
|
|
const r = parseProductDocMd(md)
|
|
expect(r.ok).toBe(false)
|
|
if (r.ok) return
|
|
expect(r.errors[0].line).toBeGreaterThan(0)
|
|
})
|
|
})
|