import { EditorSelection, EditorState } from '@codemirror/state'; import { frontMatterTagName, frontMatterContentTagName, frontMatterMarkerTagName } from './markdownFrontMatterExtension'; import createTestEditor from '../testing/createTestEditor'; import findNodesWithName from '../testing/findNodesWithName'; // Creates an EditorState with FrontMatter and markdown extensions const createEditorState = async (initialText: string, expectedTags: string[]): Promise => { return (await createTestEditor(initialText, EditorSelection.cursor(0), expectedTags)).state; }; describe('markdownFrontMatterExtension', () => { jest.retryTimes(2); it('should parse a basic FrontMatter block at the start of the document', async () => { const documentText = '---\ntitle: Test\n---\n\n# Heading'; const editor = await createEditorState(documentText, [frontMatterTagName, 'ATXHeading1']); const frontMatterNodes = findNodesWithName(editor, frontMatterTagName); expect(frontMatterNodes.length).toBe(1); expect(frontMatterNodes[0].from).toBe(0); expect(frontMatterNodes[0].to).toBe('---\ntitle: Test\n---'.length); }); it('should parse FrontMatter with multiple properties', async () => { const frontMatter = '---\ntitle: Test\ndate: 2024-01-01\ntags: [one, two]\n---'; const documentText = `${frontMatter}\n\nContent here.`; const editor = await createEditorState(documentText, [frontMatterTagName]); const frontMatterNodes = findNodesWithName(editor, frontMatterTagName); expect(frontMatterNodes.length).toBe(1); expect(frontMatterNodes[0].from).toBe(0); expect(frontMatterNodes[0].to).toBe(frontMatter.length); }); it('should not parse FrontMatter if not at document start', async () => { const documentText = 'Some text\n\n---\ntitle: Test\n---'; const editor = await createEditorState(documentText, ['Paragraph']); const frontMatterNodes = findNodesWithName(editor, frontMatterTagName); // Should not be recognized as FrontMatter since it's not at the start expect(frontMatterNodes.length).toBe(0); }); it('should not parse FrontMatter without closing delimiter', async () => { // Test document with --- at start but no closing delimiter // This should be parsed as a horizontal rule followed by content const documentText = '# Heading\n\n---\ntitle: Test'; const editor = await createEditorState(documentText, ['ATXHeading1', 'HorizontalRule']); const frontMatterNodes = findNodesWithName(editor, frontMatterTagName); // FrontMatter only works at the very start of the document, so this should not be recognized expect(frontMatterNodes.length).toBe(0); }); it('should treat the entire document as frontmatter when closing delimiter is missing (issue #14542)', async () => { const documentText = '---\nsome: frontmatter\n--\n\n# Hey'; const editor = await createEditorState(documentText, [frontMatterTagName]); const frontMatterNodes = findNodesWithName(editor, frontMatterTagName); // Frontmatter block must be recognised and span the entire document expect(frontMatterNodes.length).toBe(1); expect(frontMatterNodes[0].from).toBe(0); expect(frontMatterNodes[0].to).toBe(documentText.length); }); it('should handle empty FrontMatter block', async () => { const documentText = '---\n---\n\n# Heading'; const editor = await createEditorState(documentText, [frontMatterTagName, 'ATXHeading1']); const frontMatterNodes = findNodesWithName(editor, frontMatterTagName); expect(frontMatterNodes.length).toBe(1); expect(frontMatterNodes[0].from).toBe(0); expect(frontMatterNodes[0].to).toBe('---\n---'.length); }); it('should have FrontMatterContent as child node', async () => { const documentText = '---\nkey: value\n---'; const editor = await createEditorState(documentText, [frontMatterTagName]); const frontMatterNodes = findNodesWithName(editor, frontMatterTagName); const contentNodes = findNodesWithName(editor, frontMatterContentTagName); expect(frontMatterNodes.length).toBe(1); // Content node may be replaced by YAML parser, but if not, it should exist // The presence depends on whether YAML language was loaded expect(contentNodes.length).toBeGreaterThanOrEqual(0); }); it('should not confuse horizontal rules with FrontMatter', async () => { const documentText = '# Title\n\n---\n\nSome text'; const editor = await createEditorState(documentText, ['ATXHeading1', 'HorizontalRule']); const frontMatterNodes = findNodesWithName(editor, frontMatterTagName); const hrNodes = findNodesWithName(editor, 'HorizontalRule'); expect(frontMatterNodes.length).toBe(0); expect(hrNodes.length).toBe(1); }); it('should create FrontMatterMarker nodes for the delimiters', async () => { const documentText = '---\ntitle: Test\n---\n\n# Heading'; const editor = await createEditorState(documentText, [frontMatterTagName, frontMatterMarkerTagName]); const markerNodes = findNodesWithName(editor, frontMatterMarkerTagName); // Should have two markers: opening and closing --- expect(markerNodes.length).toBe(2); // Opening marker expect(markerNodes[0].from).toBe(0); expect(markerNodes[0].to).toBe(3); // '---'.length // Closing marker expect(markerNodes[1].from).toBe('---\ntitle: Test\n'.length); expect(markerNodes[1].to).toBe('---\ntitle: Test\n---'.length); }); });