Automerge backed Markdown editor for the Atmosphere
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155import { describe, it, expect, beforeEach } from 'vitest'import { EditorState } from '@codemirror/state'import { EditorView } from '@codemirror/view'import { MarkdownView, attachMarkdownViewEditor, detachMarkdownViewEditor } from '../views/markdown-view'import { CM6EditorAdapter } from '../cm6-editor'
function createTestView(content: string): EditorView { return new EditorView({ state: EditorState.create({ doc: content }), })}
const mockLeaf = {} as any
describe('MarkdownView + CM6EditorAdapter integration', () => { let view: MarkdownView let cm6View: EditorView let adapter: CM6EditorAdapter
beforeEach(() => { view = new MarkdownView(mockLeaf) cm6View = createTestView('# Hello\n\nSome content') adapter = new CM6EditorAdapter() adapter.setView(cm6View) attachMarkdownViewEditor(view, adapter) })
it('editor.getValue reads from CM6 state', () => { expect(view.editor.getValue()).toBe('# Hello\n\nSome content') })
it('editor.lineCount reads from CM6 state', () => { cm6View.destroy() cm6View = createTestView('a\nb\nc\nd') adapter.setView(cm6View) attachMarkdownViewEditor(view, adapter) expect(view.editor.lineCount()).toBe(4) })
it('editor.getLine reads specific line', () => { cm6View.destroy() cm6View = createTestView('# Title\n\nParagraph\n- item') adapter.setView(cm6View) attachMarkdownViewEditor(view, adapter) expect(view.editor.getLine(0)).toBe('# Title') expect(view.editor.getLine(1)).toBe('') expect(view.editor.getLine(2)).toBe('Paragraph') expect(view.editor.getLine(3)).toBe('- item') })
it('getViewData returns CM6 content when bound', () => { view.setViewData('backing data', false) expect(view.getViewData()).toBe('# Hello\n\nSome content') })
it('getViewData returns backing store when unbound', () => { detachMarkdownViewEditor(view) view.setViewData('backing data', false) expect(view.getViewData()).toBe('backing data') })
it('getViewData returns empty string when bound with empty content', () => { cm6View.destroy() cm6View = createTestView('') adapter.setView(cm6View) attachMarkdownViewEditor(view, adapter) view.setViewData('stale data', false) expect(view.getViewData()).toBe('') })
it('detach helper returns editor to unbound state', () => { expect(view.editor.getValue()).toBe('# Hello\n\nSome content') detachMarkdownViewEditor(view) expect(view.editor.getValue()).toBe('') })
it('focus/blur do not throw', () => { expect(() => view.editor.focus()).not.toThrow() expect(() => view.editor.blur()).not.toThrow() })
it('mutation methods work (setValue, replaceRange)', () => { view.editor.setValue('new content') expect(view.editor.getValue()).toBe('new content') })
it('getRange returns correct text range', () => { cm6View.destroy() cm6View = createTestView('hello\nworld\ntest') adapter.setView(cm6View) attachMarkdownViewEditor(view, adapter) const range = view.editor.getRange({ line: 0, ch: 2 }, { line: 1, ch: 3 }) expect(range).toBe('llo\nwor') })
it('posToOffset and offsetToPos are inverses', () => { cm6View.destroy() cm6View = createTestView('hello\nworld\ntest') adapter.setView(cm6View) attachMarkdownViewEditor(view, adapter) const pos = { line: 1, ch: 3 } const offset = view.editor.posToOffset(pos) const back = view.editor.offsetToPos(offset) expect(back).toEqual(pos) })
it('wordAt returns word under cursor', () => { cm6View.destroy() cm6View = createTestView('hello world') adapter.setView(cm6View) attachMarkdownViewEditor(view, adapter) const range = view.editor.wordAt({ line: 0, ch: 7 }) expect(range).toEqual({ from: { line: 0, ch: 6 }, to: { line: 0, ch: 11 }, }) })
it('replaceRange works through editor interface', () => { view.editor.replaceRange('REPLACED', { line: 0, ch: 2 }, { line: 0, ch: 7 }) expect(view.editor.getLine(0)).toBe('# REPLACED') })
it('setLine works through editor interface', () => { view.editor.setLine(0, '## Changed Heading') expect(view.editor.getLine(0)).toBe('## Changed Heading') })
it('selection operations work through editor interface', () => { view.editor.setSelection({ line: 0, ch: 2 }, { line: 0, ch: 7 }) expect(view.editor.getSelection()).toBe('Hello') expect(view.editor.somethingSelected()).toBe(true) })
it('transaction with replaceSelection works', () => { view.editor.setSelection({ line: 0, ch: 2 }, { line: 0, ch: 7 }) view.editor.transaction({ replaceSelection: 'World' }) expect(view.editor.getLine(0)).toBe('# World') })
it('editor.getDoc returns the editor itself (CM compatibility)', () => { expect(view.editor.getDoc()).toBe(view.editor) })
it('setCursor works with position object', () => { view.editor.setCursor({ line: 1, ch: 0 }) expect(view.editor.getCursor()).toEqual({ line: 1, ch: 0 }) })
it('setCursor works with line/ch numbers', () => { view.editor.setCursor(2, 3) expect(view.editor.getCursor()).toEqual({ line: 2, ch: 3 }) })})