import { change, emptyChange, init, type Doc } from '@automerge/automerge' import { describe, expect, it } from 'vitest' import { createEditorSource } from '../../src/sync/sources/editorSource.js' interface TestDoc { text?: string } function emptyDoc(): Doc { return emptyChange(init(), undefined) } function withText(text: string): Doc { return change(emptyChange(init(), undefined), (d) => { d.text = text }) } describe('editor Source (a Source that is also a Handle)', () => { it('change() updates doc(), notifies listeners, and delivers to the session', () => { const editor = createEditorSource('doc1', emptyDoc()) let notified = 0 editor.on('change', () => { notified += 1 }) const delivered: Doc[] = [] editor.subscribe((_id, doc) => { delivered.push(doc) }) editor.change((d) => { d.text = 'hi' }) expect(editor.doc().text).toBe('hi') expect(notified).toBe(1) expect((delivered[0] as Doc).text).toBe('hi') }) it('push() from the session updates doc() + notifies, but is NOT delivered back (no loop)', async () => { const editor = createEditorSource('doc1', emptyDoc()) let notified = 0 editor.on('change', () => { notified += 1 }) const delivered: unknown[] = [] editor.subscribe((_id, doc) => { delivered.push(doc) }) await editor.push('doc1', withText('remote')) expect(editor.doc().text).toBe('remote') expect(notified).toBe(1) expect(delivered.length).toBe(0) }) it('off() removes a listener', () => { const editor = createEditorSource('doc1', emptyDoc()) let n = 0 const listener = (): void => { n += 1 } editor.on('change', listener) editor.change((d) => { d.text = 'a' }) editor.off('change', listener) editor.change((d) => { d.text = 'b' }) expect(n).toBe(1) }) })