import { Node } from 'prosemirror-model'; import { describe, expect, it } from 'vitest'; import { docToHtml } from './html'; import { docToMarkdown, markdownToDoc } from './markdown'; import { docToPlainText } from './plain-text'; function markers(doc: Node): { kind: string; date: string }[] { const found: { kind: string; date: string }[] = []; doc.descendants((node) => { if (node.type.name === 'date_marker') { found.push({ kind: node.attrs.kind as string, date: node.attrs.date as string }); } }); return found; } describe('date markers >>/<< (issue #152)', () => { it('parses ISO due and start dates and round-trips canonically', () => { const doc = markdownToDoc('Projekt <<2026-07-01 bis >>2026-12-31 fertig.'); expect(markers(doc)).toEqual([ { kind: 'start', date: '2026-07-01' }, { kind: 'due', date: '2026-12-31' }, ]); const markdown = docToMarkdown(doc); expect(markdown).toContain('<<2026-07-01'); expect(markdown).toContain('>>2026-12-31'); }); it('accepts dd.mm.yyyy as input lenience but serializes ISO', () => { const doc = markdownToDoc('Zieltermin >>31.12.2026 bitte.'); expect(markers(doc)).toEqual([{ kind: 'due', date: '2026-12-31' }]); expect(docToMarkdown(doc)).toContain('>>2026-12-31'); }); it('keeps a line-leading >>date out of blockquote parsing', () => { const doc = markdownToDoc('>>2026-12-31 ist die Deadline.'); expect(markers(doc)).toEqual([{ kind: 'due', date: '2026-12-31' }]); let quotes = 0; doc.descendants((node) => { if (node.type.name === 'blockquote') quotes += 1; }); expect(quotes).toBe(0); // A real blockquote still works. const quote = markdownToDoc('> ein Zitat'); let realQuotes = 0; quote.descendants((node) => { if (node.type.name === 'blockquote') realQuotes += 1; }); expect(realQuotes).toBe(1); }); it('leaves invalid calendar dates as plain text', () => { const doc = markdownToDoc('Kaputt: >>31.02.2026 bleibt Text.'); expect(markers(doc)).toEqual([]); expect(docToMarkdown(doc)).toContain('31.02.2026'); }); it('renders HTML with kind, ISO date, and shows up in plain text', () => { const doc = markdownToDoc('Bis >>2026-12-31.'); const html = docToHtml(doc); expect(html).toContain('data-date-marker="due"'); expect(html).toContain('data-date="2026-12-31"'); expect(docToPlainText(doc)).toContain('2026-12-31'); }); it('works inside task list lines', () => { const doc = markdownToDoc('- [ ] Bühne buchen >>2026-08-01 @nadia'); expect(markers(doc)).toEqual([{ kind: 'due', date: '2026-08-01' }]); const markdown = docToMarkdown(doc); expect(markdown).toContain('- [ ] Bühne buchen >>2026-08-01 @nadia'); }); });