import * as Automerge from '@automerge/automerge'; import { describe, expect, it } from 'vitest'; import { readDocContent, spliceText, updateTextContent } from './text-diff'; type Doc = { content: string }; describe('updateTextContent', () => { it('merges concurrent character-level edits without clobbering', () => { const base = Automerge.from({ content: 'hello world' }); // Two actors fork from the same base and edit different regions. let a = Automerge.clone(base, { actor: 'aaaaaaaaaaaaaaaa' }); let b = Automerge.clone(base, { actor: 'bbbbbbbbbbbbbbbb' }); a = Automerge.change(a, (d) => updateTextContent(d, ['content'], 'HELLO world'), ); b = Automerge.change(b, (d) => updateTextContent(d, ['content'], 'hello WORLD'), ); const merged = Automerge.merge(a, b); // Character-level splices merge: both edits survive (a wholesale write // would have kept only one side). expect(readDocContent(merged.content)).toBe('HELLO WORLD'); }); it('replaces content for a single editor', () => { let doc = Automerge.from({ content: 'v1' }); doc = Automerge.change(doc, (d) => updateTextContent(d, ['content'], 'v2 longer text'), ); expect(readDocContent(doc.content)).toBe('v2 longer text'); }); it('assigns into an empty field', () => { let doc = Automerge.from({ content: '' }); doc = Automerge.change(doc, (d) => updateTextContent(d, ['content'], 'fresh'), ); expect(readDocContent(doc.content)).toBe('fresh'); }); }); describe('spliceText', () => { it('applies minimal character-level edits', () => { let doc = Automerge.from({ content: 'the quick brown fox' }); doc = Automerge.change(doc, (d) => spliceText(d, ['content'], 'the quick brown fox', 'the slow brown fox'), ); expect(readDocContent(doc.content)).toBe('the slow brown fox'); }); }); describe('readDocContent', () => { it('returns plain strings as-is', () => { expect(readDocContent('hello')).toBe('hello'); }); it('passes through binary content', () => { const bytes = new Uint8Array([1, 2, 3]); expect(readDocContent(bytes)).toBe(bytes); }); it('returns null for null/undefined', () => { expect(readDocContent(null)).toBeNull(); expect(readDocContent(undefined)).toBeNull(); }); });