import { describe, it, expect } from 'vitest'; import { parseJournalMarkdown, serializeJournalMarkdown, calculateReadingTime } from '../journal'; import type { JournalBlock } from '../journal'; const roundTrip = (blocks: JournalBlock[]) => parseJournalMarkdown( serializeJournalMarkdown({ frontmatter: {}, blocks, referencedAssetIds: [] }), ); describe('facts block', () => { it('parses a ::facts chunk into label/value items', () => { const parsed = parseJournalMarkdown('::facts\nDistance: 21 km\nElevation: 1,240 m'); expect(parsed.blocks).toEqual([ { type: 'facts', items: [ { label: 'Distance', value: '21 km' }, { label: 'Elevation', value: '1,240 m' }, ], }, ]); }); it('splits at the first colon so a time keeps its value', () => { const parsed = parseJournalMarkdown('::facts\nStart: 08:30'); expect(parsed.blocks[0]).toMatchObject({ type: 'facts', items: [{ label: 'Start', value: '08:30' }], }); }); it('ignores lines without a colon or with an empty side', () => { const parsed = parseJournalMarkdown('::facts\njust words\n: no label\nNo value:\nOk: yes'); expect(parsed.blocks[0]).toEqual({ type: 'facts', items: [{ label: 'Ok', value: 'yes' }] }); }); it('renders inline markdown in values and escapes the rest', () => { const parsed = parseJournalMarkdown( '::facts\nGuide: [Anna](https://example.com)\nNote: x', ); expect(parsed.blocks[0]).toMatchObject({ items: [ { label: 'Guide', value: 'Anna', }, { label: 'Note', value: '<b>x</b>' }, ], }); }); it('round-trips, dropping rows the editor left empty', () => { const block: JournalBlock = { type: 'facts', items: [ { label: 'Distance', value: '21 km' }, { label: '', value: '' }, { label: 'Weather', value: 'clear, **cold**' }, ], }; const parsed = roundTrip([block]); expect(parsed.blocks).toEqual([ { type: 'facts', items: [ { label: 'Distance', value: '21 km' }, { label: 'Weather', value: 'clear, cold' }, ], }, ]); // And the serialized form is stable from here on. const once = serializeJournalMarkdown(parsed); expect(serializeJournalMarkdown(parseJournalMarkdown(once))).toBe(once); expect(once).toBe('::facts\nDistance: 21 km\nWeather: clear, **cold**'); }); it('is its own block between paragraphs', () => { const parsed = parseJournalMarkdown('Intro.\n\n::facts\nA: 1\n\nOutro.'); expect(parsed.blocks.map((b) => b.type)).toEqual(['paragraph', 'facts', 'paragraph']); }); it('does not count the directive line as reading', () => { const { words } = calculateReadingTime('::facts\nDistance: 21 km'); expect(words).toBe(3); }); });