overleaf/services/web/public/coffee/tests/unit/UndoManagerTests.coffee
2014-02-12 10:23:40 +00:00

455 lines
13 KiB
CoffeeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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"