mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
747 lines
22 KiB
JavaScript
747 lines
22 KiB
JavaScript
|
'use strict'
|
||
|
|
||
|
const _ = require('lodash')
|
||
|
const { expect } = require('chai')
|
||
|
|
||
|
const ot = require('..')
|
||
|
const File = ot.File
|
||
|
const AddFileOperation = ot.AddFileOperation
|
||
|
const MoveFileOperation = ot.MoveFileOperation
|
||
|
const EditFileOperation = ot.EditFileOperation
|
||
|
const NoOperation = ot.NoOperation
|
||
|
const Operation = ot.Operation
|
||
|
const TextOperation = ot.TextOperation
|
||
|
const Snapshot = ot.Snapshot
|
||
|
|
||
|
describe('Operation', function () {
|
||
|
function makeEmptySnapshot() {
|
||
|
return new Snapshot()
|
||
|
}
|
||
|
|
||
|
function makeOneFileSnapshot() {
|
||
|
const snapshot = makeEmptySnapshot()
|
||
|
snapshot.addFile('foo', File.fromString(''))
|
||
|
return snapshot
|
||
|
}
|
||
|
|
||
|
function makeTwoFileSnapshot() {
|
||
|
const snapshot = makeOneFileSnapshot()
|
||
|
snapshot.addFile('bar', File.fromString('a'))
|
||
|
return snapshot
|
||
|
}
|
||
|
|
||
|
function addFile(pathname, content) {
|
||
|
return new AddFileOperation(pathname, File.fromString(content))
|
||
|
}
|
||
|
|
||
|
function roundTripOperation(operation) {
|
||
|
return Operation.fromRaw(operation.toRaw())
|
||
|
}
|
||
|
|
||
|
function deepCopySnapshot(snapshot) {
|
||
|
return Snapshot.fromRaw(snapshot.toRaw())
|
||
|
}
|
||
|
|
||
|
function runConcurrently(operation0, operation1, snapshot) {
|
||
|
const operations = [
|
||
|
// make sure they survive serialization
|
||
|
roundTripOperation(operation0),
|
||
|
roundTripOperation(operation1),
|
||
|
]
|
||
|
const primeOperations = Operation.transform(operation0, operation1)
|
||
|
const originalSnapshot = snapshot || makeEmptySnapshot()
|
||
|
const snapshotA = deepCopySnapshot(originalSnapshot)
|
||
|
const snapshotB = deepCopySnapshot(originalSnapshot)
|
||
|
|
||
|
operations[0].applyTo(snapshotA)
|
||
|
operations[1].applyTo(snapshotB)
|
||
|
|
||
|
primeOperations[0].applyTo(snapshotB)
|
||
|
primeOperations[1].applyTo(snapshotA)
|
||
|
expect(snapshotA).to.eql(snapshotB)
|
||
|
|
||
|
return {
|
||
|
snapshot: snapshotA,
|
||
|
operations: operations,
|
||
|
primeOperations: primeOperations,
|
||
|
|
||
|
log() {
|
||
|
console.log(this)
|
||
|
return this
|
||
|
},
|
||
|
|
||
|
expectNoTransform() {
|
||
|
expect(this.operations).to.eql(this.primeOperations)
|
||
|
return this
|
||
|
},
|
||
|
|
||
|
swap() {
|
||
|
return runConcurrently(operation1, operation0, originalSnapshot)
|
||
|
},
|
||
|
|
||
|
expectFiles(files) {
|
||
|
this.expectedFiles = files
|
||
|
expect(this.snapshot.countFiles()).to.equal(_.size(files))
|
||
|
_.forOwn(files, (expectedFile, pathname) => {
|
||
|
if (_.isString(expectedFile)) {
|
||
|
expectedFile = { content: expectedFile, metadata: {} }
|
||
|
}
|
||
|
const file = this.snapshot.getFile(pathname)
|
||
|
expect(file.getContent()).to.equal(expectedFile.content)
|
||
|
expect(file.getMetadata()).to.eql(expectedFile.metadata)
|
||
|
})
|
||
|
return this
|
||
|
},
|
||
|
|
||
|
expectSymmetry() {
|
||
|
if (!this.expectedFiles) {
|
||
|
throw new Error('must call expectFiles before expectSymmetry')
|
||
|
}
|
||
|
this.swap().expectFiles(this.expectedFiles)
|
||
|
return this
|
||
|
},
|
||
|
|
||
|
expectPrime(index, klass) {
|
||
|
expect(this.primeOperations[index]).to.be.an.instanceof(klass)
|
||
|
return this
|
||
|
},
|
||
|
|
||
|
tap(fn) {
|
||
|
fn.call(this)
|
||
|
return this
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// shorthand for creating an edit operation
|
||
|
function edit(pathname, textOperationJsonObject) {
|
||
|
return Operation.editFile(
|
||
|
pathname,
|
||
|
TextOperation.fromJSON(textOperationJsonObject)
|
||
|
)
|
||
|
}
|
||
|
|
||
|
it('transforms AddFile-AddFile with different names', function () {
|
||
|
runConcurrently(addFile('foo', ''), addFile('bar', 'a'))
|
||
|
.expectNoTransform()
|
||
|
.expectFiles({ bar: 'a', foo: '' })
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms AddFile-AddFile with same name', function () {
|
||
|
// the second file 'wins'
|
||
|
runConcurrently(addFile('foo', ''), addFile('foo', 'a'))
|
||
|
.expectFiles({ foo: 'a' })
|
||
|
// if the first add was committed first, the second add overwrites it
|
||
|
.expectPrime(1, AddFileOperation)
|
||
|
// if the second add was committed first, the first add becomes a no-op
|
||
|
.expectPrime(0, NoOperation)
|
||
|
.swap()
|
||
|
.expectFiles({ foo: '' })
|
||
|
})
|
||
|
|
||
|
it('transforms AddFile-MoveFile with no conflict', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'baz'),
|
||
|
addFile('bar', 'a'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectNoTransform()
|
||
|
.expectFiles({ bar: 'a', baz: '' })
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms AddFile-MoveFile with move from new file', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'baz'),
|
||
|
addFile('foo', 'a'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ baz: 'a' })
|
||
|
// if the move was committed first, the add overwrites it
|
||
|
.expectPrime(1, AddFileOperation)
|
||
|
// if the add was committed first, the move appears in the history
|
||
|
.expectPrime(0, MoveFileOperation)
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms AddFile-MoveFile with move to new file', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'baz'),
|
||
|
addFile('baz', 'a'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ baz: 'a' })
|
||
|
// if the move was committed first, the add overwrites it
|
||
|
.expectPrime(1, AddFileOperation)
|
||
|
// if the add was committed first, the move becomes a delete
|
||
|
.expectPrime(0, MoveFileOperation)
|
||
|
.tap(function () {
|
||
|
expect(this.primeOperations[0].isRemoveFile()).to.be.true
|
||
|
})
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms AddFile-RemoveFile with no conflict', function () {
|
||
|
runConcurrently(
|
||
|
Operation.removeFile('foo'),
|
||
|
addFile('bar', 'a'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectNoTransform()
|
||
|
.expectFiles({ bar: 'a' })
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms AddFile-RemoveFile that removes added file', function () {
|
||
|
runConcurrently(
|
||
|
Operation.removeFile('foo'),
|
||
|
addFile('foo', 'a'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ foo: 'a' })
|
||
|
// if the move was committed first, the add overwrites it
|
||
|
.expectPrime(1, AddFileOperation)
|
||
|
// if the add was committed first, the move gets dropped
|
||
|
.expectPrime(0, NoOperation)
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms AddFile-EditFile with no conflict', function () {
|
||
|
runConcurrently(
|
||
|
edit('foo', ['x']),
|
||
|
addFile('bar', 'a'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectNoTransform()
|
||
|
.expectFiles({ bar: 'a', foo: 'x' })
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms AddFile-EditFile when new file is edited', function () {
|
||
|
runConcurrently(
|
||
|
edit('foo', ['x']),
|
||
|
addFile('foo', 'a'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ foo: 'a' })
|
||
|
// if the edit was committed first, the add overwrites it
|
||
|
.expectPrime(1, AddFileOperation)
|
||
|
// if the add was committed first, the edit gets dropped
|
||
|
.expectPrime(0, NoOperation)
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms AddFile-SetFileMetadata with no conflict', function () {
|
||
|
const testMetadata = { baz: 1 }
|
||
|
runConcurrently(
|
||
|
addFile('bar', 'a'),
|
||
|
Operation.setFileMetadata('foo', testMetadata),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectNoTransform()
|
||
|
.expectFiles({ foo: { content: '', metadata: testMetadata }, bar: 'a' })
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms AddFile-SetFileMetadata with same name', function () {
|
||
|
const testMetadata = { baz: 1 }
|
||
|
runConcurrently(
|
||
|
addFile('foo', 'x'),
|
||
|
Operation.setFileMetadata('foo', testMetadata),
|
||
|
makeEmptySnapshot()
|
||
|
)
|
||
|
.expectFiles({ foo: { content: 'x', metadata: testMetadata } })
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-MoveFile with no conflict', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'baz'),
|
||
|
Operation.moveFile('bar', 'bat'),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ bat: 'a', baz: '' })
|
||
|
.expectNoTransform()
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-MoveFile same move foo->foo, foo->foo', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'foo'),
|
||
|
Operation.moveFile('foo', 'foo'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ foo: '' })
|
||
|
.expectPrime(1, NoOperation)
|
||
|
.expectPrime(0, NoOperation)
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-MoveFile no-op foo->foo, foo->bar', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'foo'),
|
||
|
Operation.moveFile('foo', 'bar'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ bar: '' })
|
||
|
.expectPrime(1, MoveFileOperation)
|
||
|
.expectPrime(0, NoOperation)
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-MoveFile no-op foo->foo, bar->foo', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'foo'),
|
||
|
Operation.moveFile('foo', 'bar'),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ bar: '' })
|
||
|
.expectPrime(1, MoveFileOperation)
|
||
|
.expectPrime(0, NoOperation)
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-MoveFile no-op foo->foo, bar->bar', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'foo'),
|
||
|
Operation.moveFile('bar', 'bar'),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ bar: 'a', foo: '' })
|
||
|
.expectPrime(1, NoOperation)
|
||
|
.expectPrime(0, NoOperation)
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-MoveFile same move foo->bar, foo->bar', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'bar'),
|
||
|
Operation.moveFile('foo', 'bar'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ bar: '' })
|
||
|
.expectPrime(1, NoOperation)
|
||
|
.expectPrime(0, NoOperation)
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-MoveFile opposite foo->bar, bar->foo', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'bar'),
|
||
|
Operation.moveFile('bar', 'foo'),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles([])
|
||
|
.expectPrime(1, MoveFileOperation)
|
||
|
.expectPrime(0, MoveFileOperation)
|
||
|
.tap(function () {
|
||
|
expect(this.primeOperations[1].isRemoveFile()).to.be.true
|
||
|
expect(this.primeOperations[1].getPathname()).to.equal('bar')
|
||
|
|
||
|
expect(this.primeOperations[0].isRemoveFile()).to.be.true
|
||
|
expect(this.primeOperations[0].getPathname()).to.equal('foo')
|
||
|
})
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-MoveFile no-op foo->foo, bar->baz', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'foo'),
|
||
|
Operation.moveFile('bar', 'baz'),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ baz: 'a', foo: '' })
|
||
|
.expectPrime(1, MoveFileOperation)
|
||
|
.expectPrime(0, NoOperation)
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-MoveFile diverge foo->bar, foo->baz', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'bar'),
|
||
|
Operation.moveFile('foo', 'baz'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ baz: '' })
|
||
|
// if foo->bar was committed first, the second move becomes bar->baz
|
||
|
.expectPrime(1, MoveFileOperation)
|
||
|
// if foo->baz was committed first, the second move becomes a no-op
|
||
|
.expectPrime(0, NoOperation)
|
||
|
.tap(function () {
|
||
|
expect(this.primeOperations[1].getPathname()).to.equal('bar')
|
||
|
expect(this.primeOperations[1].getNewPathname()).to.equal('baz')
|
||
|
})
|
||
|
.swap()
|
||
|
.expectFiles({ bar: '' })
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-MoveFile transitive foo->baz, bar->foo', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'baz'),
|
||
|
Operation.moveFile('bar', 'foo'),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ baz: 'a' })
|
||
|
.expectPrime(1, MoveFileOperation)
|
||
|
.expectPrime(0, MoveFileOperation)
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-MoveFile transitive foo->bar, bar->baz', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'bar'),
|
||
|
Operation.moveFile('bar', 'baz'),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ baz: '' })
|
||
|
.expectPrime(1, MoveFileOperation)
|
||
|
.expectPrime(0, MoveFileOperation)
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-MoveFile converge foo->baz, bar->baz', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'baz'),
|
||
|
Operation.moveFile('bar', 'baz'),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ baz: 'a' })
|
||
|
.expectPrime(1, MoveFileOperation)
|
||
|
.expectPrime(0, MoveFileOperation)
|
||
|
.tap(function () {
|
||
|
// if foo->baz was committed first, we just apply the move
|
||
|
expect(this.primeOperations[1]).to.eql(this.operations[1])
|
||
|
|
||
|
// if bar->baz was committed first, the other move becomes a remove
|
||
|
expect(this.primeOperations[0].isRemoveFile()).to.be.true
|
||
|
expect(this.primeOperations[0].getPathname()).to.equal('foo')
|
||
|
})
|
||
|
.swap()
|
||
|
.expectFiles({ baz: '' })
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-RemoveFile no-op foo->foo, foo->', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'foo'),
|
||
|
Operation.removeFile('foo'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles([])
|
||
|
.expectPrime(1, MoveFileOperation)
|
||
|
.expectPrime(0, NoOperation)
|
||
|
.tap(function () {
|
||
|
expect(this.primeOperations[1].isRemoveFile()).to.be.true
|
||
|
})
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-RemoveFile same move foo->, foo->', function () {
|
||
|
runConcurrently(
|
||
|
Operation.removeFile('foo'),
|
||
|
Operation.removeFile('foo'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles([])
|
||
|
.expectPrime(1, NoOperation)
|
||
|
.expectPrime(0, NoOperation)
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-RemoveFile no conflict foo->, bar->', function () {
|
||
|
runConcurrently(
|
||
|
Operation.removeFile('foo'),
|
||
|
Operation.removeFile('bar'),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles([])
|
||
|
.expectNoTransform()
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-RemoveFile no conflict foo->foo, bar->', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'foo'),
|
||
|
Operation.removeFile('bar'),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ foo: '' })
|
||
|
.expectPrime(1, MoveFileOperation)
|
||
|
.expectPrime(0, NoOperation)
|
||
|
.tap(function () {
|
||
|
expect(this.primeOperations[1].isRemoveFile()).to.be.true
|
||
|
})
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-RemoveFile transitive foo->, bar->foo', function () {
|
||
|
runConcurrently(
|
||
|
Operation.removeFile('foo'),
|
||
|
Operation.moveFile('bar', 'foo'),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles([])
|
||
|
.expectPrime(1, MoveFileOperation)
|
||
|
.expectPrime(0, MoveFileOperation)
|
||
|
.tap(function () {
|
||
|
expect(this.primeOperations[1].isRemoveFile()).to.be.true
|
||
|
expect(this.primeOperations[1].getPathname()).to.equal('bar')
|
||
|
|
||
|
expect(this.primeOperations[0].isRemoveFile()).to.be.true
|
||
|
expect(this.primeOperations[0].getPathname()).to.equal('foo')
|
||
|
})
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-RemoveFile transitive foo->bar, bar->', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'bar'),
|
||
|
Operation.removeFile('bar'),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({})
|
||
|
.expectPrime(1, MoveFileOperation)
|
||
|
.expectPrime(0, MoveFileOperation)
|
||
|
.tap(function () {
|
||
|
expect(this.primeOperations[1].isRemoveFile()).to.be.true
|
||
|
expect(this.primeOperations[1].getPathname()).to.equal('bar')
|
||
|
|
||
|
expect(this.primeOperations[0].isRemoveFile()).to.be.true
|
||
|
expect(this.primeOperations[0].getPathname()).to.equal('foo')
|
||
|
})
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-EditFile with no conflict', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('bar', 'baz'),
|
||
|
edit('foo', ['x']),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ baz: 'a', foo: 'x' })
|
||
|
.expectNoTransform()
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-EditFile with edit on pathname', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'bar'),
|
||
|
edit('foo', ['x']),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ bar: 'x' })
|
||
|
.expectPrime(1, EditFileOperation)
|
||
|
.expectPrime(0, MoveFileOperation)
|
||
|
.tap(function () {
|
||
|
expect(this.primeOperations[1].getPathname()).to.equal('bar')
|
||
|
|
||
|
expect(this.primeOperations[0].getPathname()).to.equal('foo')
|
||
|
expect(this.primeOperations[0].getNewPathname()).to.equal('bar')
|
||
|
})
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-EditFile with edit on new pathname', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('bar', 'foo'),
|
||
|
edit('foo', ['x']),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ foo: 'a' })
|
||
|
.expectPrime(1, NoOperation)
|
||
|
.tap(function () {
|
||
|
expect(this.primeOperations[0]).to.eql(this.operations[0])
|
||
|
})
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-EditFile with no-op move', function () {
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'foo'),
|
||
|
edit('foo', ['x']),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ foo: 'x' })
|
||
|
.expectNoTransform()
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-SetFileMetadata with no conflict', function () {
|
||
|
const testMetadata = { baz: 1 }
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'baz'),
|
||
|
Operation.setFileMetadata('bar', testMetadata),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectNoTransform()
|
||
|
.expectFiles({ bar: { content: 'a', metadata: testMetadata }, baz: '' })
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-SetFileMetadata with set on pathname', function () {
|
||
|
const testMetadata = { baz: 1 }
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'bar'),
|
||
|
Operation.setFileMetadata('foo', testMetadata),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ bar: { content: '', metadata: testMetadata } })
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-SetFileMetadata w/ set on new pathname', function () {
|
||
|
const testMetadata = { baz: 1 }
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'bar'),
|
||
|
Operation.setFileMetadata('bar', testMetadata),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
// move wins
|
||
|
.expectFiles({ bar: { content: '', metadata: {} } })
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms MoveFile-SetFileMetadata with no-op move', function () {
|
||
|
const testMetadata = { baz: 1 }
|
||
|
runConcurrently(
|
||
|
Operation.moveFile('foo', 'foo'),
|
||
|
Operation.setFileMetadata('foo', testMetadata),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ foo: { content: '', metadata: testMetadata } })
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms EditFile-EditFile with no conflict', function () {
|
||
|
runConcurrently(
|
||
|
edit('foo', ['x']),
|
||
|
edit('bar', [1, 'x']),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ bar: 'ax', foo: 'x' })
|
||
|
.expectNoTransform()
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms EditFile-EditFile on same file', function () {
|
||
|
runConcurrently(
|
||
|
edit('foo', ['x']),
|
||
|
edit('foo', ['y']),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ foo: 'xy' })
|
||
|
.expectPrime(1, EditFileOperation)
|
||
|
.expectPrime(0, EditFileOperation)
|
||
|
.tap(function () {
|
||
|
expect(this.primeOperations[1].getTextOperation().toJSON()).to.eql([
|
||
|
1,
|
||
|
'y',
|
||
|
])
|
||
|
expect(this.primeOperations[0].getTextOperation().toJSON()).to.eql([
|
||
|
'x',
|
||
|
1,
|
||
|
])
|
||
|
})
|
||
|
.swap()
|
||
|
.expectFiles({ foo: 'yx' })
|
||
|
})
|
||
|
|
||
|
it('transforms EditFile-RemoveFile with no conflict', function () {
|
||
|
runConcurrently(
|
||
|
edit('foo', ['x']),
|
||
|
Operation.removeFile('bar'),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({ foo: 'x' })
|
||
|
.expectNoTransform()
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms EditFile-RemoveFile on same file', function () {
|
||
|
runConcurrently(
|
||
|
edit('foo', ['x']),
|
||
|
Operation.removeFile('foo'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({})
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms EditFile-SetFileMetadata with no conflict', function () {
|
||
|
const testMetadata = { baz: 1 }
|
||
|
runConcurrently(
|
||
|
edit('foo', ['x']),
|
||
|
Operation.setFileMetadata('bar', testMetadata),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectNoTransform()
|
||
|
.expectFiles({
|
||
|
foo: { content: 'x', metadata: {} },
|
||
|
bar: { content: 'a', metadata: testMetadata },
|
||
|
})
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms EditFile-SetFileMetadata on same file', function () {
|
||
|
const testMetadata = { baz: 1 }
|
||
|
runConcurrently(
|
||
|
edit('foo', ['x']),
|
||
|
Operation.setFileMetadata('foo', testMetadata),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectNoTransform()
|
||
|
.expectFiles({ foo: { content: 'x', metadata: testMetadata } })
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms SetFileMetadata-SetFileMetadata w/ no conflict', function () {
|
||
|
runConcurrently(
|
||
|
Operation.setFileMetadata('foo', { baz: 1 }),
|
||
|
Operation.setFileMetadata('bar', { baz: 2 }),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectNoTransform()
|
||
|
.expectFiles({
|
||
|
foo: { content: '', metadata: { baz: 1 } },
|
||
|
bar: { content: 'a', metadata: { baz: 2 } },
|
||
|
})
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms SetFileMetadata-SetFileMetadata on same file', function () {
|
||
|
runConcurrently(
|
||
|
Operation.setFileMetadata('foo', { baz: 1 }),
|
||
|
Operation.setFileMetadata('foo', { baz: 2 }),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
// second op wins
|
||
|
.expectFiles({ foo: { content: '', metadata: { baz: 2 } } })
|
||
|
.swap()
|
||
|
// first op wins
|
||
|
.expectFiles({ foo: { content: '', metadata: { baz: 1 } } })
|
||
|
})
|
||
|
|
||
|
it('transforms SetFileMetadata-RemoveFile with no conflict', function () {
|
||
|
const testMetadata = { baz: 1 }
|
||
|
runConcurrently(
|
||
|
Operation.setFileMetadata('foo', testMetadata),
|
||
|
Operation.removeFile('bar'),
|
||
|
makeTwoFileSnapshot()
|
||
|
)
|
||
|
.expectNoTransform()
|
||
|
.expectFiles({ foo: { content: '', metadata: testMetadata } })
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
|
||
|
it('transforms SetFileMetadata-RemoveFile on same file', function () {
|
||
|
const testMetadata = { baz: 1 }
|
||
|
runConcurrently(
|
||
|
Operation.setFileMetadata('foo', testMetadata),
|
||
|
Operation.removeFile('foo'),
|
||
|
makeOneFileSnapshot()
|
||
|
)
|
||
|
.expectFiles({})
|
||
|
.expectSymmetry()
|
||
|
})
|
||
|
})
|