Merge pull request #26209 from overleaf/em-multiple-edit-ops

Support multiple ops in the history OT ShareJS type

GitOrigin-RevId: fad1e9081ed1978de414c5130692d3b23fcd13d8
This commit is contained in:
Eric Mc Sween
2025-06-10 08:06:13 +00:00
committed by Copybot
parent 5b08adc4ff
commit 6d202432ff
2 changed files with 169 additions and 4 deletions
@@ -0,0 +1,134 @@
import { expect } from 'chai'
import {
StringFileData,
TextOperation,
AddCommentOperation,
Range,
} from 'overleaf-editor-core'
import { historyOTType } from '@/features/ide-react/editor/share-js-history-ot-type'
describe('historyOTType', function () {
let snapshot: StringFileData
let opsA: TextOperation[]
let opsB: TextOperation[]
beforeEach(function () {
snapshot = new StringFileData('one plus two equals three')
// After opsA: "seven plus five equals twelve"
opsA = [new TextOperation(), new TextOperation(), new TextOperation()]
opsA[0].remove(3)
opsA[0].insert('seven')
opsA[0].retain(22)
opsA[1].retain(11)
opsA[1].remove(3)
opsA[1].insert('five')
opsA[1].retain(13)
opsA[2].retain(23)
opsA[2].remove(5)
opsA[2].insert('twelve')
// After ops2: "one times two equals two"
opsB = [new TextOperation(), new TextOperation()]
opsB[0].retain(4)
opsB[0].remove(4)
opsB[0].insert('times')
opsB[0].retain(17)
opsB[1].retain(21)
opsB[1].remove(5)
opsB[1].insert('two')
})
describe('apply', function () {
it('supports an empty operations array', function () {
const result = historyOTType.apply(snapshot, [])
expect(result.getContent()).to.equal('one plus two equals three')
})
it('applies operations to the snapshot (opsA)', function () {
const result = historyOTType.apply(snapshot, opsA)
expect(result.getContent()).to.equal('seven plus five equals twelve')
})
it('applies operations to the snapshot (opsB)', function () {
const result = historyOTType.apply(snapshot, opsB)
expect(result.getContent()).to.equal('one times two equals two')
})
})
describe('compose', function () {
it('supports empty operations', function () {
const ops = historyOTType.compose([], [])
expect(ops).to.deep.equal([])
})
it('supports an empty operation on the left', function () {
const ops = historyOTType.compose([], opsA)
const result = historyOTType.apply(snapshot, ops)
expect(result.getContent()).to.equal('seven plus five equals twelve')
})
it('supports an empty operation on the right', function () {
const ops = historyOTType.compose(opsA, [])
const result = historyOTType.apply(snapshot, ops)
expect(result.getContent()).to.equal('seven plus five equals twelve')
})
it('supports operations on both sides', function () {
const ops = historyOTType.compose(opsA.slice(0, 2), opsA.slice(2))
const result = historyOTType.apply(snapshot, ops)
expect(ops.length).to.equal(1)
expect(result.getContent()).to.equal('seven plus five equals twelve')
})
it("supports operations that can't be composed", function () {
const comment = new AddCommentOperation('comment-id', [new Range(3, 10)])
const ops = historyOTType.compose(opsA.slice(0, 2), [
comment,
...opsA.slice(2),
])
expect(ops.length).to.equal(3)
const result = historyOTType.apply(snapshot, ops)
expect(result.getContent()).to.equal('seven plus five equals twelve')
})
})
describe('transformX', function () {
it('supports empty operations', function () {
const [aPrime, bPrime] = historyOTType.transformX([], [])
expect(aPrime).to.deep.equal([])
expect(bPrime).to.deep.equal([])
})
it('supports an empty operation on the left', function () {
const [aPrime, bPrime] = historyOTType.transformX([], opsB)
expect(aPrime).to.deep.equal([])
expect(bPrime).to.deep.equal(opsB)
})
it('supports an empty operation on the right', function () {
const [aPrime, bPrime] = historyOTType.transformX(opsA, [])
expect(aPrime).to.deep.equal(opsA)
expect(bPrime).to.deep.equal([])
})
it('supports operations on both sides (a then b)', function () {
const [, bPrime] = historyOTType.transformX(opsA, opsB)
const ops = historyOTType.compose(opsA, bPrime)
const result = historyOTType.apply(snapshot, ops)
expect(result.getContent()).to.equal('seven times five equals twelvetwo')
})
it('supports operations on both sides (b then a)', function () {
const [aPrime] = historyOTType.transformX(opsA, opsB)
const ops = historyOTType.compose(opsB, aPrime)
const result = historyOTType.apply(snapshot, ops)
expect(result.getContent()).to.equal('seven times five equals twelvetwo')
})
})
})