mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-03 05:49:32 -05:00
455 lines
13 KiB
CoffeeScript
455 lines
13 KiB
CoffeeScript
define [
|
||
"undo/UndoManager"
|
||
], (UndoManager) ->
|
||
describe "UndoManager", ->
|
||
beforeEach ->
|
||
@undoManager = new UndoManager()
|
||
|
||
describe "Regression Tests", ->
|
||
describe "block commenting", ->
|
||
it "should convert correctly", ->
|
||
lines = ["a", "a", "a", "a"]
|
||
simpleDeltas = [
|
||
deltas: [
|
||
{ insert: "%", position: 0 }
|
||
{ insert: "%", position: 3 }
|
||
{ insert: "%", position: 6 }
|
||
{ insert: "%", position: 9 }
|
||
]
|
||
group: "doc"
|
||
]
|
||
aceDeltas = @undoManager._simpleDeltaSetsToAceDeltaSets(simpleDeltas, lines)
|
||
expectedAceDeltas = [
|
||
deltas: [{
|
||
action: "insertText"
|
||
text: "%"
|
||
range:
|
||
start: column: 0, row: 0
|
||
end: column: 1, row: 0
|
||
}, {
|
||
action: "insertText"
|
||
text: "%"
|
||
range:
|
||
start: column: 0, row: 1
|
||
end: column: 1, row: 1
|
||
}, {
|
||
action: "insertText"
|
||
text: "%"
|
||
range:
|
||
start: column: 0, row: 2
|
||
end: column: 1, row: 2
|
||
}, {
|
||
action: "insertText"
|
||
text: "%"
|
||
range:
|
||
start: column: 0, row: 3
|
||
end: column: 1, row: 3
|
||
}]
|
||
group: "doc"
|
||
]
|
||
console.log aceDeltas, expectedAceDeltas
|
||
aceDeltas.should.deep.equal expectedAceDeltas
|
||
|
||
describe "_shiftLocalChangeToTopOfUndoStack", ->
|
||
describe "with no local undos", ->
|
||
beforeEach ->
|
||
@undoManager.undoStack = [
|
||
{ deltaSets: [], remote: true }
|
||
{ deltaSets: [], remote: true }
|
||
]
|
||
@return = @undoManager._shiftLocalChangeToTopOfUndoStack()
|
||
|
||
it "should return false", ->
|
||
@return.should.equal false
|
||
|
||
describe "with a local undo that can be shifted to the top", ->
|
||
beforeEach ->
|
||
@undoManager.undoStack = [
|
||
{ deltaSets: [deltas: [{ position: 0, insert: "banana"}], group: "doc"], remote: true }
|
||
{ deltaSets: [deltas: [{ position: 5, insert: "baz" }], group: "doc"], remote: false }
|
||
{ deltaSets: [deltas: [{ position: 20, insert: "bar" }], group: "doc"], remote: true }
|
||
{ deltaSets: [deltas: [{ position: 10, insert: "foo" }], group: "doc"], remote: true }
|
||
]
|
||
@return = @undoManager._shiftLocalChangeToTopOfUndoStack()
|
||
|
||
it "should bring the local change to the top of the stack", ->
|
||
@expected = [
|
||
{ deltaSets: [deltas: [{ position: 0, insert: "banana"}], group: "doc"], remote: true }
|
||
{ deltaSets: [deltas: [{ position: 17, insert: "bar" }], group: "doc"], remote: true }
|
||
{ deltaSets: [deltas: [{ position: 7, insert: "foo" }], group: "doc"], remote: true }
|
||
{ deltaSets: [deltas: [{ position: 5, insert: "baz" }], group: "doc"], remote: false }
|
||
]
|
||
@undoManager.undoStack.should.deep.equal @expected
|
||
|
||
it "should return true", ->
|
||
@return.should.equal true
|
||
|
||
describe "with a local undo that cannot be brought all the way to the top", ->
|
||
beforeEach ->
|
||
@undoManager.undoStack = [
|
||
{ deltaSets: [deltas: [{ position: 0, insert: "banana"}], group: "doc"], remote: true }
|
||
{ deltaSets: [deltas: [{ position: 5, insert: "baz" }], group: "doc"], remote: false }
|
||
{ deltaSets: [deltas: [{ position: 20, insert: "bar" }], group: "doc"], remote: true }
|
||
{ deltaSets: [deltas: [{ position: 6, insert: "foo" }], group: "doc"], remote: true }
|
||
]
|
||
@return = @undoManager._shiftLocalChangeToTopOfUndoStack()
|
||
|
||
it "should bring the change as far up the stack as possible", ->
|
||
@expected = [
|
||
{ deltaSets: [deltas: [{ position: 0, insert: "banana"}], group: "doc"], remote: true }
|
||
{ deltaSets: [deltas: [{ position: 17, insert: "bar" }], group: "doc"], remote: true }
|
||
{ deltaSets: [deltas: [{ position: 5, insert: "baz" }], group: "doc"], remote: false }
|
||
{ deltaSets: [deltas: [{ position: 6, insert: "foo" }], group: "doc"], remote: true }
|
||
]
|
||
@undoManager.undoStack.should.deep.equal @expected
|
||
|
||
it "should return true", ->
|
||
@return.should.equal true
|
||
|
||
describe "_aceDeltaToSimpleDelta", ->
|
||
beforeEach ->
|
||
@docLines = [
|
||
"one",
|
||
"two",
|
||
"three",
|
||
"four"
|
||
]
|
||
|
||
describe "insertText", ->
|
||
it "should convert correctly", ->
|
||
@aceDelta =
|
||
action: "insertText"
|
||
range:
|
||
start:
|
||
row: 2
|
||
column: 2
|
||
end:
|
||
row: 2
|
||
column: 5
|
||
text: "foo"
|
||
@simpleDelta = @undoManager._aceDeltaToSimpleDelta(@aceDelta, @docLines)
|
||
@simpleDelta.should.deep.equal
|
||
insert: "foo"
|
||
position: 10
|
||
|
||
describe "insertLines", ->
|
||
it "should convert correctly", ->
|
||
@aceDelta =
|
||
action: "insertLines"
|
||
lines: [
|
||
"two and half"
|
||
"two and three quarters"
|
||
]
|
||
range:
|
||
start:
|
||
row: 2
|
||
column: 0
|
||
end:
|
||
row: 4
|
||
column: 0
|
||
@simpleDelta = @undoManager._aceDeltaToSimpleDelta(@aceDelta, @docLines)
|
||
@simpleDelta.should.deep.equal
|
||
insert: "two and half\ntwo and three quarters\n"
|
||
position: 8
|
||
|
||
describe "removeText", ->
|
||
it "should convert correctly", ->
|
||
@aceDelta =
|
||
action: "removeLines"
|
||
range:
|
||
start:
|
||
row: 1
|
||
column: 0
|
||
end:
|
||
row: 3
|
||
column: 0
|
||
lines: [
|
||
"two",
|
||
"three"
|
||
]
|
||
@simpleDelta = @undoManager._aceDeltaToSimpleDelta(@aceDelta, @docLines)
|
||
@simpleDelta.should.deep.equal
|
||
remove: "two\nthree\n"
|
||
position: 4
|
||
|
||
describe "_simpleDeltaToAceDelta", ->
|
||
describe "insert", ->
|
||
beforeEach ->
|
||
@docLines = [
|
||
"one",
|
||
"two"
|
||
"three"
|
||
]
|
||
describe "with leading and trailing partial lines", ->
|
||
it "should return insertText and insertLines ace updates", ->
|
||
@simpleDelta =
|
||
position: 7
|
||
insert: "after\ntwo and a half\nbefore"
|
||
@aceDeltas = @undoManager._simpleDeltaToAceDeltas(@simpleDelta, @docLines)
|
||
@expected = [{
|
||
text: "after"
|
||
range:
|
||
start: row: 1, column: 3
|
||
end: row:1, column: 8
|
||
action: "insertText"
|
||
}, {
|
||
text: "\n"
|
||
range:
|
||
start: row: 1, column: 8
|
||
end: row:2, column: 0
|
||
action: "insertText"
|
||
}, {
|
||
lines: [ "two and a half" ]
|
||
range:
|
||
start: row: 2, column:0
|
||
end: row:3, column: 0
|
||
action: "insertLines"
|
||
}, {
|
||
text: "before"
|
||
range:
|
||
start: row: 3, column:0
|
||
end: row: 3, column: 6
|
||
action: "insertText"
|
||
}]
|
||
@aceDeltas.should.deep.equal @expected
|
||
|
||
describe "remove", ->
|
||
beforeEach ->
|
||
@docLines = [
|
||
"one",
|
||
"two"
|
||
"three"
|
||
]
|
||
describe "with leading and trailing partial lines", ->
|
||
it "should return insertText and insertLines ace updates", ->
|
||
@simpleDelta =
|
||
position: 2
|
||
remove: "e\ntwo\nth"
|
||
@aceDeltas = @undoManager._simpleDeltaToAceDeltas(@simpleDelta, @docLines)
|
||
@expected = [{
|
||
text: "th"
|
||
range:
|
||
start: row: 2, column:0
|
||
end: row: 2, column: 2
|
||
action: "removeText"
|
||
}, {
|
||
lines: [ "two" ]
|
||
range:
|
||
start: row: 1, column:0
|
||
end: row:2, column: 0
|
||
action: "removeLines"
|
||
}, {
|
||
text: "\n"
|
||
range:
|
||
start: row: 0, column: 3
|
||
end: row:1, column: 0
|
||
action: "removeText"
|
||
}, {
|
||
text: "e"
|
||
range:
|
||
start: row: 0, column: 2
|
||
end: row: 0, column: 3
|
||
action: "removeText"
|
||
}]
|
||
@aceDeltas.should.deep.equal @expected
|
||
|
||
describe "_concatSimpleDeltas", ->
|
||
it "should concat adjacent simple deltas", ->
|
||
@result = @undoManager._concatSimpleDeltas [{
|
||
insert: "foo"
|
||
position: 5
|
||
}, {
|
||
insert: "bar"
|
||
position: 8
|
||
}, {
|
||
insert: "baz"
|
||
position: 11
|
||
}, {
|
||
insert: "one"
|
||
position: 20
|
||
}, {
|
||
insert: "two"
|
||
position: 23
|
||
}, {
|
||
remove: "three"
|
||
position: 26
|
||
}]
|
||
|
||
@result.should.deep.equal [{
|
||
insert: "foobarbaz"
|
||
position: 5
|
||
}, {
|
||
insert: "onetwo"
|
||
position: 20
|
||
}, {
|
||
remove: "three"
|
||
position: 26
|
||
}]
|
||
|
||
it "should concat removes", ->
|
||
@result = @undoManager._concatSimpleDeltas [{
|
||
remove: "foo"
|
||
position: 5
|
||
}, {
|
||
remove: "bar"
|
||
position: 5
|
||
}, {
|
||
remove: "baz"
|
||
position: 5
|
||
}]
|
||
|
||
@result.should.deep.equal [{
|
||
remove: "foobarbaz"
|
||
position: 5
|
||
}]
|
||
|
||
describe "_swapSimpleDeltaOrder", ->
|
||
describe "insert - insert", ->
|
||
describe "when the first delta is before the second", ->
|
||
beforeEach ->
|
||
# result: "**fooba"
|
||
[@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
|
||
{ insert: "foo", position: 2 }
|
||
{ insert: "ba", position: 5 }
|
||
)
|
||
|
||
it "should remove the length of the first insert from the second position", ->
|
||
@newFirstDelta.should.deep.equal position: 2, insert: "ba"
|
||
@newSecondDelta.should.deep.equal position: 2, insert: "foo"
|
||
|
||
describe "when the second delta is inserted into the first", ->
|
||
beforeEach ->
|
||
# result "**fobao*"
|
||
@result = @undoManager._swapSimpleDeltaOrder(
|
||
{ insert: "foo", position: 2 }
|
||
{ insert: "ba", position: 4 }
|
||
)
|
||
|
||
it "should return null", ->
|
||
(@result?).should.equal false
|
||
|
||
describe "when the second delta is before the first", ->
|
||
beforeEach ->
|
||
# result: "**bafoo"
|
||
[@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
|
||
{ insert: "foo", position: 2 }
|
||
{ insert: "ba", position: 2 }
|
||
)
|
||
|
||
it "should add the length of the second delta on to the first position", ->
|
||
@newFirstDelta.should.deep.equal insert: "ba", position: 2
|
||
@newSecondDelta.should.deep.equal insert: "foo", position: 4
|
||
|
||
describe "remove - remove", ->
|
||
describe "when the first delta is before the second", ->
|
||
beforeEach ->
|
||
# start "**fooba*
|
||
[@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
|
||
{ remove: "foo", position: 2 }
|
||
{ remove: "ba", position: 2 }
|
||
)
|
||
|
||
it "should add the length of the first remove onto the second position", ->
|
||
@newFirstDelta.should.deep.equal position: 5, remove: "ba"
|
||
@newSecondDelta.should.deep.equal position: 2, remove: "foo"
|
||
|
||
describe "when the first and second delta overlap", ->
|
||
beforeEach ->
|
||
# start "**bfooa*
|
||
@result = @undoManager._swapSimpleDeltaOrder(
|
||
{ remove: "foo", position: 3 }
|
||
{ remove: "ba", position: 2 }
|
||
)
|
||
|
||
it "should return null", ->
|
||
(@result?).should.equal false
|
||
|
||
describe "when the second delta is before the first", ->
|
||
beforeEach ->
|
||
# start "**bafoo*
|
||
[@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
|
||
{ remove: "foo", position: 4 }
|
||
{ remove: "ba", position: 2 }
|
||
)
|
||
|
||
it "should remove the length of the second delta from the first position", ->
|
||
@newFirstDelta.should.deep.equal position: 2, remove: "ba"
|
||
@newSecondDelta.should.deep.equal position: 2, remove: "foo"
|
||
|
||
describe "insert - remove", ->
|
||
describe "when the first delta is before the second", ->
|
||
beforeEach ->
|
||
# "**ba" -> "**fooba" -> "**foo"
|
||
[@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
|
||
{ insert: "foo", position: 2 }
|
||
{ remove: "ba", position: 5 }
|
||
)
|
||
|
||
it "should remove the length of the first delta from the second position", ->
|
||
@newFirstDelta.should.deep.equal position: 2, remove: "ba"
|
||
@newSecondDelta.should.deep.equal position: 2, insert: "foo"
|
||
|
||
describe "when the deltas overlap", ->
|
||
beforeEach ->
|
||
# "**ba" -> "**bfooa" -> "**ooa"
|
||
@result = @undoManager._swapSimpleDeltaOrder(
|
||
{ insert: "foo", position: 3 }
|
||
{ remove: "bf", position: 2 }
|
||
)
|
||
|
||
it "should return null", ->
|
||
(@result?).should.equal false
|
||
|
||
describe "when the second delta is before the first", ->
|
||
beforeEach ->
|
||
# "**ba" -> "**bafoo" -> "**foo"
|
||
[@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
|
||
{ insert: "foo", position: 4 }
|
||
{ remove: "ba", position: 2 }
|
||
)
|
||
|
||
it "should remove the length of the second delta from the first position", ->
|
||
@newFirstDelta.should.deep.equal position: 2, remove: "ba"
|
||
@newSecondDelta.should.deep.equal position: 2, insert: "foo"
|
||
|
||
describe "remove - insert", ->
|
||
describe "when the first delta is before the second", ->
|
||
beforeEach ->
|
||
# "**foo" -> "**" -> "**ba"
|
||
[@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
|
||
{ remove: "foo", position: 2 }
|
||
{ insert: "ba", position: 2 }
|
||
)
|
||
|
||
it "should add the length of the first delta to the second position", ->
|
||
@newFirstDelta.should.deep.equal position: 5, insert: "ba"
|
||
@newSecondDelta.should.deep.equal position: 2, remove: "foo"
|
||
|
||
# I don't think the deltas can overlap in this case!
|
||
|
||
describe "when the first delta is after the second", ->
|
||
beforeEach ->
|
||
# "**foo" -> "**" -> "*ba*"
|
||
[@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
|
||
{ remove: "foo", position: 2 }
|
||
{ insert: "ba", position: 1 }
|
||
)
|
||
|
||
it "should add the length of the second delta on to the first position", ->
|
||
@newFirstDelta.should.deep.equal position: 1, insert: "ba"
|
||
@newSecondDelta.should.deep.equal position: 4, remove: "foo"
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|